#Requires AutoHotkey v2.0

#Include MultiDiaplayMonitors.ahk

; support ahk2 -- 02/09/2024
; ---------------
; STATUS ENUMERATION
;
; Ok =						= 0
; GenericError				= 1
; InvalidParameter			= 2
; OutOfMemory				= 3
; ObjectBusy				= 4
; InsufficientBuffer		= 5
; NotImplemented			= 6
; Win32Error				= 7
; WrongState				= 8
; Aborted					= 9
; FileNotFound				= 10
; ValueOverflow				= 11
; AccessDenied				= 12
; UnknownImageFormat		= 13
; FontFamilyNotFound		= 14
; FontStyleNotFound			= 15
; NotTrueTypeFont			= 16
; UnsupportedGdiplusVersion	= 17
; GdiplusNotInitialized		= 18
; PropertyNotFound			= 19
; PropertyNotSupported		= 20
; ProfileNotFound			= 21
;-------------------


; Function:					UpdateLayeredWindow
; Description:				Updates a layered window with the handle to the DC of a gdi bitmap
;
; hwnd						Handle of the layered window to update
; hdc						Handle to the DC of the GDI bitmap to update the window with
; Layeredx					x position to place the window
; Layeredy					y position to place the window
; Layeredw					Width of the window
; Layeredh					Height of the window
; Alpha						Default = 255 : The transparency (0-255) to set the window transparency
;
; return					If the function succeeds, the return value is nonzero
;
; notes						If x or y omitted, then layered window will use its current coordinates
;							If w or h omitted then current width and height will be used
UpdateLayeredWindow(hwnd, hdc, x := '', y := '', w := '', h := '', Alpha := 255) {
  pt := ''
  if x != '' && y != '' {
    pt := Buffer(8)
    NumPut('uint', x, pt, 0), NumPut('uint', y, pt, 4)
  }
  if w = '' || h = '' {
    CreateRect(&winRect, 0, 0, 0, 0)
    DllCall("GetWindowRect", 'ptr', hwnd, 'ptr', winRect.Ptr)
    w := NumGet(winRect, 8, "UInt") - NumGet(winRect, 0, "UInt")
    h := NumGet(winRect, 12, "UInt") - NumGet(winRect, 4, "UInt")
  }

  return DllCall("UpdateLayeredWindow"
    , 'ptr', hwnd
    , 'ptr', 0
    , 'ptr', ((x = "") && (y = "") ? 0 : pt)
    , "int64*", w | h << 32
    , 'ptr', hdc
    , "int64*", 0
    , "uint", 0
    , "UInt*", Alpha << 16 | 1 << 24
    , "uint", 2)
}


; Function				BitBlt
; Description			The BitBlt function performs a bit-block transfer of the color data corresponding to a rectangle
;						of pixels from the specified source device context into a destination device context.
;
; dDC					handle to destination DC
; dx					x-coord of destination upper-left corner
; dy					y-coord of destination upper-left corner
; dw					width of the area to copy
; dh					height of the area to copy
; sDC					handle to source DC
; sx					x-coordinate of source upper-left corner
; sy					y-coordinate of source upper-left corner
; Raster				raster operation code
;
; return				If the function succeeds, the return value is nonzero
;
; notes					If no raster operation is specified, then SRCCOPY is used, which copies the source directly to the destination rectangle
;
; BLACKNESS				= 0x00000042
; NOTSRCERASE			= 0x001100A6
; NOTSRCCOPY			= 0x00330008
; SRCERASE				= 0x00440328
; DSTINVERT				= 0x00550009
; PATINVERT				= 0x005A0049
; SRCINVERT				= 0x00660046
; SRCAND				= 0x008800C6
; MERGEPAINT			= 0x00BB0226
; MERGECOPY				= 0x00C000CA
; SRCCOPY				= 0x00CC0020
; SRCPAINT				= 0x00EE0086
; PATCOPY				= 0x00F00021
; PATPAINT				= 0x00FB0A09
; WHITENESS				= 0x00FF0062
; CAPTUREBLT			= 0x40000000
; NOMIRRORBITMAP		= 0x80000000
BitBlt(ddc, dx, dy, dw, dh, sdc, sx, sy, Raster := "") {
  return DllCall("gdi32\BitBlt"
    , 'uptr', dDC, "int", dx, "int", dy, "int", dw, "int", dh
    , 'uptr', sDC, "int", sx, "int", sy
    , "uint", Raster ? Raster : 0x00CC0020)
}


; Function				StretchBlt
; Description			The StretchBlt function copies a bitmap from a source rectangle into a destination rectangle,
;						stretching or compressing the bitmap to fit the dimensions of the destination rectangle, if necessary.
;						The system stretches or compresses the bitmap according to the stretching mode currently set in the destination device context.
;
; ddc					handle to destination DC
; dx					x-coord of destination upper-left corner
; dy					y-coord of destination upper-left corner
; dw					width of destination rectangle
; dh					height of destination rectangle
; sdc					handle to source DC
; sx					x-coordinate of source upper-left corner
; sy					y-coordinate of source upper-left corner
; sw					width of source rectangle
; sh					height of source rectangle
; Raster				raster operation code
;
; return				If the function succeeds, the return value is nonzero
;
; notes					If no raster operation is specified, then SRCCOPY is used. It uses the same raster operations as BitBlt
StretchBlt(ddc, dx, dy, dw, dh, sdc, sx, sy, sw, sh, Raster := "") {
  Ptr := A_PtrSize ? "UPtr" : "UInt"
  return DllCall("gdi32\StretchBlt"
    , Ptr, ddc, "int", dx, "int", dy, "int", dw, "int", dh
    , Ptr, sdc, "int", sx, "int", sy, "int", sw, "int", sh
    , "uint", Raster ? Raster : 0x00CC0020)
}


; Function				SetStretchBltMode
; Description			The SetStretchBltMode function sets the bitmap stretching mode in the specified device context
;
; hdc					handle to the DC
; iStretchMode			The stretching mode, describing how the target will be stretched
;
; return				If the function succeeds, the return value is the previous stretching mode. If it fails it will return 0
;
; STRETCH_ANDSCANS 		= 0x01
; STRETCH_ORSCANS 		= 0x02
; STRETCH_DELETESCANS 	= 0x03
; STRETCH_HALFTONE 		= 0x04
SetStretchBltMode(hdc, iStretchMode := 4) => DllCall("gdi32\SetStretchBltMode", "UPtr", hdc, "int", iStretchMode)


; Function				SetImage
; Description			Associates a new image with a static control
;
; hwnd					handle of the control to update
; hBitmap				a gdi bitmap to associate the static control with
;
; return				If the function succeeds, the return value is nonzero
SetImage(hwnd, hBitmap) {
  E := DllCall("SendMessage", "UPtr", hwnd, "UInt", 0x172, "UInt", 0x0, "UPtr", hBitmap)
  DeleteObject(E)
  return E
}


; Function				SetSysColorToControl
; Description			Sets a solid colour to a control
;
; hwnd					handle of the control to update
; SysColor				A system colour to set to the control
;
; return				If the function succeeds, the return value is zero
;
; notes					A control must have the 0xE style set to it so it is recognised as a bitmap
;						By default SysColor=15 is used which is COLOR_3DFACE. This is the standard background for a control
;
; COLOR_3DDKSHADOW				= 21
; COLOR_3DFACE					= 15
; COLOR_3DHIGHLIGHT				= 20
; COLOR_3DHILIGHT				= 20
; COLOR_3DLIGHT					= 22
; COLOR_3DSHADOW				= 16
; COLOR_ACTIVEBORDER			= 10
; COLOR_ACTIVECAPTION			= 2
; COLOR_APPWORKSPACE			= 12
; COLOR_BACKGROUND				= 1
; COLOR_BTNFACE					= 15
; COLOR_BTNHIGHLIGHT			= 20
; COLOR_BTNHILIGHT				= 20
; COLOR_BTNSHADOW				= 16
; COLOR_BTNTEXT					= 18
; COLOR_CAPTIONTEXT				= 9
; COLOR_DESKTOP					= 1
; COLOR_GRADIENTACTIVECAPTION	= 27
; COLOR_GRADIENTINACTIVECAPTION	= 28
; COLOR_GRAYTEXT				= 17
; COLOR_HIGHLIGHT				= 13
; COLOR_HIGHLIGHTTEXT			= 14
; COLOR_HOTLIGHT				= 26
; COLOR_INACTIVEBORDER			= 11
; COLOR_INACTIVECAPTION			= 3
; COLOR_INACTIVECAPTIONTEXT		= 19
; COLOR_INFOBK					= 24
; COLOR_INFOTEXT				= 23
; COLOR_MENU					= 4
; COLOR_MENUHILIGHT				= 29
; COLOR_MENUBAR					= 30
; COLOR_MENUTEXT				= 7
; COLOR_SCROLLBAR				= 0
; COLOR_WINDOW					= 5
; COLOR_WINDOWFRAME				= 6
; COLOR_WINDOWTEXT				= 8
SetSysColorToControl(hwnd, SysColor := 15) {
  CreateRect(&winRect, 0, 0, 0, 0)
  DllCall("GetWindowRect", 'uptr', hwnd, 'uptr', winRect.Ptr)
  w := NumGet(winRect, 8, "UInt") - NumGet(winRect, 0, "UInt")
  h := NumGet(winRect, 12, "UInt") - NumGet(winRect, 4, "UInt")
  bc := DllCall("GetSysColor", "Int", SysColor, "UInt")
  pBrushClear := Gdip_BrushCreateSolid(0xff000000 | (bc >> 16 | bc & 0xff00 | (bc & 0xff) << 16))
  pBitmap := Gdip_CreateBitmap(w, h), G := Gdip_GraphicsFromImage(pBitmap)
  Gdip_FillRectangle(G, pBrushClear, 0, 0, w, h)
  hBitmap := Gdip_CreateHBITMAPFromBitmap(pBitmap)
  SetImage(hwnd, hBitmap)
  Gdip_DeleteBrush(pBrushClear)
  Gdip_DeleteGraphics(G), Gdip_DisposeImage(pBitmap), DeleteObject(hBitmap)
  return 0
}

; Function				Gdip_BitmapFromScreen
; Description			Gets a gdi+ bitmap from the screen
;
; Screen				0 = All screens
;						Any numerical value = Just that screen
;						x|y|w|h = Take specific coordinates with a width and height
; Raster				raster operation code
;
; return					If the function succeeds, the return value is a pointer to a gdi+ bitmap
;						-1:		one or more of x,y,w,h not passed properly
;
; notes					If no raster operation is specified, then SRCCOPY is used to the returned bitmap
Gdip_BitmapFromScreen(Screen := 0, Raster := "") {
  hhdc := 0
  if Screen = 0 {
    _x := DllCall("GetSystemMetrics", "Int", 76)
    _y := DllCall("GetSystemMetrics", "Int", 77)
    _w := DllCall("GetSystemMetrics", "Int", 78)
    _h := DllCall("GetSystemMetrics", "Int", 79)
  } else if SubStr(Screen, 1, 5) = "hwnd:" {
    Screen := SubStr(Screen, 6)
    if !WinExist("ahk_id " Screen)
      return -2
    CreateRect(&winRect, 0, 0, 0, 0)
    DllCall("GetWindowRect", 'uptr', Screen, 'uptr', winRect.Ptr)
    _w := NumGet(winRect, 8, "UInt") - NumGet(winRect, 0, "UInt")
    _h := NumGet(winRect, 12, "UInt") - NumGet(winRect, 4, "UInt")
    _x := _y := 0
    hhdc := GetDCEx(Screen, 3)
  } else if IsInteger(Screen) {
    M := GetMonitorInfo(Screen)
    _x := M.Left, _y := M.Top, _w := M.Right - M.Left, _h := M.Bottom - M.Top
  } else {
    S := StrSplit(Screen, "|")
    _x := S[1], _y := S[2], _w := S[3], _h := S[4]
  }
  if (_x = "") || (_y = "") || (_w = "") || (_h = "")
    return -1
  chdc := CreateCompatibleDC(), hbm := CreateDIBSection(_w, _h, chdc), obm := SelectObject(chdc, hbm), hhdc := hhdc ? hhdc : GetDC()
  BitBlt(chdc, 0, 0, _w, _h, hhdc, _x, _y, Raster)
  ReleaseDC(hhdc)
  pBitmap := Gdip_CreateBitmapFromHBITMAP(hbm)
  SelectObject(chdc, obm), DeleteObject(hbm), DeleteDC(hhdc), DeleteDC(chdc)
  return pBitmap
}


; Function				Gdip_BitmapFromHWND
; Description			Uses PrintWindow to get a handle to the specified window and return a bitmap from it
;
; hwnd					handle to the window to get a bitmap from
;
; return				If the function succeeds, the return value is a pointer to a gdi+ bitmap
;
; notes					Window must not be not minimised in order to get a handle to it's client area
Gdip_BitmapFromHWND(hwnd) {
  CreateRect(&winRect, 0, 0, 0, 0)
  DllCall("GetWindowRect", 'uptr', hwnd, 'uptr', winRect.Ptr)
  Width := NumGet(winRect, 8, "UInt") - NumGet(winRect, 0, "UInt")
  Height := NumGet(winRect, 12, "UInt") - NumGet(winRect, 4, "UInt")
  hbm := CreateDIBSection(Width, Height), hdc := CreateCompatibleDC(), obm := SelectObject(hdc, hbm)
  PrintWindow(hwnd, hdc)
  pBitmap := Gdip_CreateBitmapFromHBITMAP(hbm)
  SelectObject(hdc, obm), DeleteObject(hbm), DeleteDC(hdc)
  return pBitmap
}


; Function				CreateRectF
; Description			Creates a RectF object, containing a the coordinates and dimensions of a rectangle
;
; RectF					Name to call the RectF object
; x						x-coordinate of the upper left corner of the rectangle
; y						y-coordinate of the upper left corner of the rectangle
; w						Width of the rectangle
; h						Height of the rectangle
;
; return				No return value
CreateRectF(&RectF, x, y, w, h) {
  RectF := Buffer(16)
  NumPut('float', x, RectF, 0)
  NumPut('float', y, RectF, 4)
  NumPut('float', w, RectF, 8)
  NumPut('float', h, RectF, 12)
}


; Function				CreateRect
; Description			Creates a Rect object, containing a the coordinates and dimensions of a rectangle
;
; RectF		 			Name to call the RectF object
; x						x-coordinate of the upper left corner of the rectangle
; y						y-coordinate of the upper left corner of the rectangle
; w						Width of the rectangle
; h						Height of the rectangle
;
; return				No return value
CreateRect(&Rect, x, y, w, h) {
  Rect := Buffer(16)
  NumPut('uint', x, Rect, 0)
  NumPut('uint', y, Rect, 4)
  NumPut('uint', w, Rect, 8)
  NumPut('uint', h, Rect, 12)
}

; Function				CreateSizeF
; Description			Creates a SizeF object, containing an 2 values
;
; SizeF					Name to call the SizeF object
; w						w-value for the SizeF object
; h						h-value for the SizeF object
;
; return				No Return value
CreateSizeF(&SizeF, w, h) {
  SizeF := Buffer(8)
  NumPut('float', w, SizeF, 0)
  NumPut('float', h, SizeF, 4)
}

; Function				CreatePointF
; Description			Creates a SizeF object, containing an 2 values
;
; SizeF					Name to call the SizeF object
; w						w-value for the SizeF object
; h						h-value for the SizeF object
;
; return				No Return value
CreatePointF(&PointF, x, y) {
  PointF := Buffer(8)
  NumPut('float', x, PointF, 0)
  NumPut('float', y, PointF, 4)
}


; Function				CreateDIBSection
; Description			The CreateDIBSection function creates a DIB (Device Independent Bitmap) that applications can write to directly
;
; w						width of the bitmap to create
; h						height of the bitmap to create
; hdc					a handle to the device context to use the palette from
; bpp					bits per pixel (32 = ARGB)
; ppvBits				A pointer to a variable that receives a pointer to the location of the DIB bit values
;
; return				returns a DIB. A gdi bitmap
;
; notes					ppvBits will receive the location of the pixels in the DIB
CreateDIBSection(w, h, hdc := "", bpp := 32, &ppvBits := 0) {
  _hdc := hdc ? hdc : GetDC()
  bi := Buffer(40, 0)
  NumPut('uint', 40, bi, 0)
  NumPut('uint', w, bi, 4)
  NumPut('uint', h, bi, 8)
  NumPut('ushort', 1, bi, 12)
  NumPut('ushort', bpp, bi, 14)
  NumPut('uint', 0, bi, 16)

  hbm := DllCall("CreateDIBSection"
    , 'uptr', _hdc
    , 'ptr', bi.Ptr
    , "uint", 0
    , "uptr*", ppvBits
    , 'uptr', 0
    , "uint", 0)
  if !hdc {
    ReleaseDC(_hdc)
  }
  return hbm
}

; Function				PrintWindow
; Description			The PrintWindow function copies a visual window into the specified device context (DC), typically a printer DC
;
; hwnd					A handle to the window that will be copied
; hdc					A handle to the device context
; Flags					Drawing options
;
; return				If the function succeeds, it returns a nonzero value
;
; PW_CLIENTONLY			= 1
PrintWindow(hwnd, hdc, Flags := 0) => DllCall("PrintWindow", 'uptr', hwnd, 'uptr', hdc, "uint", Flags)

; Function				DestroyIcon
; Description			Destroys an icon and frees any memory the icon occupied
;
; hIcon					Handle to the icon to be destroyed. The icon must not be in use
;
; return				If the function succeeds, the return value is nonzero
DestroyIcon(hIcon) => DllCall("DestroyIcon", "UPtr", hIcon)


; Function:				GetIconDimensions
; Description:			Retrieves a given icon/cursor's width and height
;
; hIcon					Pointer to an icon or cursor
; Width					ByRef variable. This variable is set to the icon's width
; Height				ByRef variable. This variable is set to the icon's height
;
; return				If the function succeeds, the return value is zero, otherwise:
;						-1 = Could not retrieve the icon's info. Check A_LastError for extended information
;						-2 = Could not delete the icon's bitmask bitmap
;						-3 = Could not delete the icon's color bitmap
GetIconDimensions(hIcon, &Width, &Height) {
  Width := Height := 0
  ICONINFO := Buffer(size := 16 + 2 * A_PtrSize, 0)
  if !DllCall("user32\GetIconInfo", 'uptr', hIcon, 'uptr', ICONINFO)
    return -1
  hbmMask := NumGet(ICONINFO.Ptr, 16, 'uptr')
  hbmColor := NumGet(ICONINFO.Ptr, 16 + A_PtrSize, 'uptr')
  BITMAP := Buffer(size, 0)
  if DllCall("gdi32\GetObject", 'uptr', hbmColor, "Int", size, 'uptr', BITMAP) {
    Width := NumGet(BITMAP, 4, "Int")
    Height := NumGet(BITMAP, 8, "Int")
  }
  if !DllCall("gdi32\DeleteObject", 'uptr', hbmMask)
    return -2
  if !DllCall("gdi32\DeleteObject", 'uptr', hbmColor)
    return -3
  return 0
}

PaintDesktop(hdc) => DllCall("PaintDesktop", "UPtr", hdc)
CreateCompatibleBitmap(hdc, w, h) => DllCall("gdi32\CreateCompatibleBitmap", 'uptr', hdc, "int", w, "int", h)

; Function				CreateCompatibleDC
; Description			This function creates a memory device context (DC) compatible with the specified device
;
; hdc					Handle to an existing device context
;
; return				returns the handle to a device context or 0 on failure
;
; notes					If this handle is 0 (by default), the function creates a memory device context compatible with the application's current screen
CreateCompatibleDC(hdc := 0) => DllCall("CreateCompatibleDC", 'uptr', hdc)


; Function				SelectObject
; Description			The SelectObject function selects an object into the specified device context (DC). The new object replaces the previous object of the same type
;
; hdc					Handle to a DC
; hgdiobj				A handle to the object to be selected into the DC
;
; return				If the selected object is not a region and the function succeeds, the return value is a handle to the object being replaced
;
; notes					The specified object must have been created by using one of the following functions
;						Bitmap - CreateBitmap, CreateBitmapIndirect, CreateCompatibleBitmap, CreateDIBitmap, CreateDIBSection (A single bitmap cannot be selected into more than one DC at the same time)
;						Brush - CreateBrushIndirect, CreateDIBPatternBrush, CreateDIBPatternBrushPt, CreateHatchBrush, CreatePatternBrush, CreateSolidBrush
;						Font - CreateFont, CreateFontIndirect
;						Pen - CreatePen, CreatePenIndirect
;						Region - CombineRgn, CreateEllipticRgn, CreateEllipticRgnIndirect, CreatePolygonRgn, CreateRectRgn, CreateRectRgnIndirect
;
; notes					If the selected object is a region and the function succeeds, the return value is one of the following value
;
; SIMPLEREGION			= 2 Region consists of a single rectangle
; COMPLEXREGION			= 3 Region consists of more than one rectangle
; NULLREGION			= 1 Region is empty
SelectObject(hdc, hgdiobj) => DllCall("SelectObject", 'ptr', hdc, 'ptr', hgdiobj)

; Function				DeleteObject
; Description			This function deletes a logical pen, brush, font, bitmap, region, or palette, freeing all system resources associated with the object
;						After the object is deleted, the specified handle is no longer valid
;
; hObject				Handle to a logical pen, brush, font, bitmap, region, or palette to delete
;
; return				Nonzero indicates success. Zero indicates that the specified handle is not valid or that the handle is currently selected into a device context
DeleteObject(hObject) => DllCall("DeleteObject", 'uptr', hObject)

; Function				GetDC
; Description			This function retrieves a handle to a display device context (DC) for the client area of the specified window.
;						The display device context can be used in subsequent graphics display interface (GDI) functions to draw in the client area of the window.
;
; hwnd					Handle to the window whose device context is to be retrieved. If this value is NULL, GetDC retrieves the device context for the entire screen
;
; return				The handle the device context for the specified window's client area indicates success. NULL indicates failure
GetDC(hwnd := 0) => DllCall("GetDC", 'uptr', hwnd)


; DCX_CACHE = 0x2
; DCX_CLIPCHILDREN = 0x8
; DCX_CLIPSIBLINGS = 0x10
; DCX_EXCLUDERGN = 0x40
; DCX_EXCLUDEUPDATE = 0x100
; DCX_INTERSECTRGN = 0x80
; DCX_INTERSECTUPDATE = 0x200
; DCX_LOCKWINDOWUPDATE = 0x400
; DCX_NORECOMPUTE = 0x100000
; DCX_NORESETATTRS = 0x4
; DCX_PARENTCLIP = 0x20
; DCX_VALIDATE = 0x200000
; DCX_WINDOW = 0x1
GetDCEx(hwnd, flags := 0, hrgnClip := 0) => DllCall("GetDCEx", 'uptr', hwnd, 'uptr', hrgnClip, "int", flags)


; Function				ReleaseDC
; Description			This function releases a device context (DC), freeing it for use by other applications. The effect of ReleaseDC depends on the type of device context
;
; hdc					Handle to the device context to be released
; hwnd					Handle to the window whose device context is to be released
;
; return				1 = released
;						0 = not released
;
; notes					The application must call the ReleaseDC function for each call to the GetWindowDC function and for each call to the GetDC function that retrieves a common device context
;						An application cannot use the ReleaseDC function to release a device context that was created by calling the CreateDC function; instead, it must use the DeleteDC function.
ReleaseDC(hdc, hwnd := 0) => DllCall("ReleaseDC", 'uptr', hwnd, 'uptr', hdc)


; Function				DeleteDC
; Description			The DeleteDC function deletes the specified device context (DC)
;
; hdc					A handle to the device context
;
; return				If the function succeeds, the return value is nonzero
;
; notes					An application must not delete a DC whose handle was obtained by calling the GetDC function. Instead, it must call the ReleaseDC function to free the DC
DeleteDC(hdc) => DllCall("DeleteDC", 'uptr', hdc)

; Function				Gdip_LibraryVersion
; Description			Get the current library version
;
; return				the library version
;
; notes					This is useful for non compiled programs to ensure that a person doesn't run an old version when testing your scripts
Gdip_LibraryVersion() => 1.45


; Function				Gdip_LibrarySubVersion
; Description			Get the current library sub version
;
; return				the library sub version
;
; notes					This is the sub-version currently maintained by Rseding91
; 					Updated by guest3456 preliminary AHK v2 support
Gdip_LibrarySubVersion() => 1.54

; Function:				Gdip_BitmapFromBRA
; Description: 			Gets a pointer to a gdi+ bitmap from a BRA file
;
; BRAFromMemIn			The variable for a BRA file read to memory
; File					The name of the file, or its number that you would like (This depends on alternate parameter)
; Alternate				Changes whether the File parameter is the file name or its number
;
; return					If the function succeeds, the return value is a pointer to a gdi+ bitmap
;						-1 = The BRA variable is empty
;						-2 = The BRA has an incorrect header
;						-3 = The BRA has information missing
;						-4 = Could not find file inside the BRA

Gdip_BitmapFromBRA(&BRAFromMemIn, File, Alternate := 0) {
  If !(BRAFromMemIn)
    Return -1
  Headers := StrSplit(StrGet(BRAFromMemIn.Ptr, 256, "CP0"), "`n")
  Header := StrSplit(Headers[1], "|")
  HeaderLength := Header.Length
  If (HeaderLength != 4) || (Header[2] != "BRA!")
    Return -2
  _Info := StrSplit(Headers[2], "|")
  _InfoLength := _Info.Length
  If (_InfoLength != 3)
    Return -3
  OffsetTOC := StrPut(Headers[1], "CP0") + StrPut(Headers[2], "CP0") ;  + 2
  OffsetData := _Info[2]
  SearchIndex := Alternate ? 1 : 2
  TOC := StrGet(BRAFromMemIn.Ptr + OffsetTOC, OffsetData - OffsetTOC - 1, "CP0")
  RX1 := "mi`n)^"
  Offset := Size := 0
  If RegExMatch(TOC, RX1 . (Alternate ? File "\|.+?" : "\d+\|" . File) . "\|(\d+)\|(\d+)$", &FileInfo) {
    Offset := OffsetData + FileInfo[1]
    Size := FileInfo[2]
  }
  If (Size = 0)
    Return -4
  hData := DllCall("GlobalAlloc", "UInt", 2, "UInt", Size, "UPtr")
  pData := DllCall("GlobalLock", "Ptr", hData, "UPtr")
  DllCall("RtlMoveMemory", "Ptr", pData, "Ptr", BRAFromMemIn.Ptr + Offset, "Ptr", Size)
  DllCall("GlobalUnlock", "Ptr", hData)
  DllCall("Ole32.dll\CreateStreamOnHGlobal", "Ptr", hData, "Int", 1, "PtrP", &pStream := 0)
  DllCall("Gdiplus.dll\GdipCreateBitmapFromStream", "Ptr", pStream, "PtrP", &pBitmap := 0)
  ObjRelease(pStream)
  Return pBitmap
}


; Function:				Gdip_BitmapFromBase64
; Description:			Creates a bitmap from a Base64 encoded string
;
; Base64				ByRef variable. Base64 encoded string. Immutable, ByRef to avoid performance overhead of passing long strings.
;
; return				If the function succeeds, the return value is a pointer to a bitmap, otherwise:
;						-1 = Could not calculate the length of the required buffer
;						-2 = Could not decode the Base64 encoded string
;						-3 = Could not create a memory stream
Gdip_BitmapFromBase64(&Base64) {
  if !(DllCall("crypt32\CryptStringToBinary", 'uptr', StrPtr(Base64), "UInt", 0, "UInt", 0x01, 'uptr', 0, "UIntP", &DecLen := 0, 'uptr', 0, 'uptr', 0))
    return -1
  Dec := Buffer(DecLen, 0)
  if !(DllCall("crypt32\CryptStringToBinary", 'uptr', StrPtr(Base64), "UInt", 0, "UInt", 0x01, 'uptr', Dec.Ptr, "UIntP", DecLen, 'uptr', 0, 'uptr', 0))
    return -2
  if !(pStream := DllCall("shlwapi\SHCreateMemStream", 'uptr', Dec.Ptr, "UInt", DecLen, "UPtr"))
    return -3
  DllCall("gdiplus\GdipCreateBitmapFromStreamICM", 'uptr', pStream, "PtrP", &pBitmap := 0)
  ObjRelease(pStream)
  return pBitmap
}


; Function				Gdip_DrawRectangle
; Description			This function uses a pen to draw the outline of a rectangle into the Graphics of a bitmap
;
; pGraphics				Pointer to the Graphics of a bitmap
; pPen					Pointer to a pen
; x						x-coordinate of the top left of the rectangle
; y						y-coordinate of the top left of the rectangle
; w						width of the rectanlge
; h						height of the rectangle
;
; return				status enumeration. 0 = success
;
; notes					as all coordinates are taken from the top left of each pixel, then the entire width/height should be specified as subtracting the pen width
Gdip_DrawRectangle(pGraphics, pPen, x, y, w, h) =>
  DllCall("gdiplus\GdipDrawRectangle"
    , 'uptr', pGraphics, 'uptr', pPen, "float", x, "float", y, "float", w, "float", h)


; Function				Gdip_DrawRoundedRectangle
; Description			This function uses a pen to draw the outline of a rounded rectangle into the Graphics of a bitmap
;
; pGraphics				Pointer to the Graphics of a bitmap
; pPen					Pointer to a pen
; x						x-coordinate of the top left of the rounded rectangle
; y						y-coordinate of the top left of the rounded rectangle
; w						width of the rectanlge
; h						height of the rectangle
; r						radius of the rounded corners
;
; return				status enumeration. 0 = success
;
; notes					as all coordinates are taken from the top left of each pixel, then the entire width/height should be specified as subtracting the pen width
Gdip_DrawRoundedRectangle(pGraphics, pPen, x, y, w, h, r) {
  Gdip_SetClipRect(pGraphics, x - r, y - r, 2 * r, 2 * r, 4)
  Gdip_SetClipRect(pGraphics, x + w - r, y - r, 2 * r, 2 * r, 4)
  Gdip_SetClipRect(pGraphics, x - r, y + h - r, 2 * r, 2 * r, 4)
  Gdip_SetClipRect(pGraphics, x + w - r, y + h - r, 2 * r, 2 * r, 4)
  _E := Gdip_DrawRectangle(pGraphics, pPen, x, y, w, h)
  Gdip_ResetClip(pGraphics)
  Gdip_SetClipRect(pGraphics, x - (2 * r), y + r, w + (4 * r), h - (2 * r), 4)
  Gdip_SetClipRect(pGraphics, x + r, y - (2 * r), w - (2 * r), h + (4 * r), 4)
  Gdip_DrawEllipse(pGraphics, pPen, x, y, 2 * r, 2 * r)
  Gdip_DrawEllipse(pGraphics, pPen, x + w - (2 * r), y, 2 * r, 2 * r)
  Gdip_DrawEllipse(pGraphics, pPen, x, y + h - (2 * r), 2 * r, 2 * r)
  Gdip_DrawEllipse(pGraphics, pPen, x + w - (2 * r), y + h - (2 * r), 2 * r, 2 * r)
  Gdip_ResetClip(pGraphics)
  return _E
}


; Function				Gdip_DrawEllipse
; Description			This function uses a pen to draw the outline of an ellipse into the Graphics of a bitmap
;
; pGraphics				Pointer to the Graphics of a bitmap
; pPen					Pointer to a pen
; x						x-coordinate of the top left of the rectangle the ellipse will be drawn into
; y						y-coordinate of the top left of the rectangle the ellipse will be drawn into
; w						width of the ellipse
; h						height of the ellipse
;
; return				status enumeration. 0 = success
;
; notes					as all coordinates are taken from the top left of each pixel, then the entire width/height should be specified as subtracting the pen width
Gdip_DrawEllipse(pGraphics, pPen, x, y, w, h) =>
  DllCall("gdiplus\GdipDrawEllipse"
    , 'uptr', pGraphics, 'uptr', pPen, "float", x, "float", y, "float", w, "float", h)


; Function				Gdip_DrawBezier
; Description			This function uses a pen to draw the outline of a bezier (a weighted curve) into the Graphics of a bitmap
;
; pGraphics				Pointer to the Graphics of a bitmap
; pPen					Pointer to a pen
; x1					x-coordinate of the start of the bezier
; y1					y-coordinate of the start of the bezier
; x2					x-coordinate of the first arc of the bezier
; y2					y-coordinate of the first arc of the bezier
; x3					x-coordinate of the second arc of the bezier
; y3					y-coordinate of the second arc of the bezier
; x4					x-coordinate of the end of the bezier
; y4					y-coordinate of the end of the bezier
;
; return				status enumeration. 0 = success
;
; notes					as all coordinates are taken from the top left of each pixel, then the entire width/height should be specified as subtracting the pen width
Gdip_DrawBezier(pGraphics, pPen, x1, y1, x2, y2, x3, y3, x4, y4) {
  return DllCall("gdiplus\GdipDrawBezier"
    , 'uptr', pgraphics
    , 'uptr', pPen
    , "float", x1
    , "float", y1
    , "float", x2
    , "float", y2
    , "float", x3
    , "float", y3
    , "float", x4
    , "float", y4)
}


; Function				Gdip_DrawArc
; Description			This function uses a pen to draw the outline of an arc into the Graphics of a bitmap
;
; pGraphics				Pointer to the Graphics of a bitmap
; pPen					Pointer to a pen
; x						x-coordinate of the start of the arc
; y						y-coordinate of the start of the arc
; w						width of the arc
; h						height of the arc
; StartAngle			specifies the angle between the x-axis and the starting point of the arc
; SweepAngle			specifies the angle between the starting and ending points of the arc
;
; return				status enumeration. 0 = success
;
; notes					as all coordinates are taken from the top left of each pixel, then the entire width/height should be specified as subtracting the pen width
Gdip_DrawArc(pGraphics, pPen, x, y, w, h, StartAngle, SweepAngle) {
  return DllCall("gdiplus\GdipDrawArc"
    , 'ptr', pGraphics
    , 'ptr', pPen
    , "float", x
    , "float", y
    , "float", w
    , "float", h
    , "float", StartAngle
    , "float", SweepAngle)
}


; Function				Gdip_DrawPie
; Description			This function uses a pen to draw the outline of a pie into the Graphics of a bitmap
;
; pGraphics				Pointer to the Graphics of a bitmap
; pPen					Pointer to a pen
; x						x-coordinate of the start of the pie
; y						y-coordinate of the start of the pie
; w						width of the pie
; h						height of the pie
; StartAngle			specifies the angle between the x-axis and the starting point of the pie
; SweepAngle			specifies the angle between the starting and ending points of the pie
;
; return				status enumeration. 0 = success
;
; notes					as all coordinates are taken from the top left of each pixel, then the entire width/height should be specified as subtracting the pen width
Gdip_DrawPie(pGraphics, pPen, x, y, w, h, StartAngle, SweepAngle) {
  return DllCall("gdiplus\GdipDrawPie", 'uptr', pGraphics, 'uptr', pPen, "float", x, "float", y, "float", w, "float", h, "float", StartAngle, "float", SweepAngle)
}

; Function				Gdip_DrawLine
; Description			This function uses a pen to draw a line into the Graphics of a bitmap
;
; pGraphics				Pointer to the Graphics of a bitmap
; pPen					Pointer to a pen
; x1					x-coordinate of the start of the line
; y1					y-coordinate of the start of the line
; x2					x-coordinate of the end of the line
; y2					y-coordinate of the end of the line
;
; return				status enumeration. 0 = success
Gdip_DrawLine(pGraphics, pPen, x1, y1, x2, y2) {
  return DllCall("gdiplus\GdipDrawLine"
    , 'uptr', pGraphics
    , 'uptr', pPen
    , "float", x1
    , "float", y1
    , "float", x2
    , "float", y2)
}


; Function				Gdip_DrawLines
; Description			This function uses a pen to draw a series of joined lines into the Graphics of a bitmap
;
; pGraphics				Pointer to the Graphics of a bitmap
; pPen					Pointer to a pen
; Points				the coordinates of all the points passed as x1,y1|x2,y2|x3,y3.....
;
; return				status enumeration. 0 = success
Gdip_DrawLines(pGraphics, pPen, Points) {
  Points := StrSplit(Points, "|")
  PointF := Buffer(8 * Points.Length)
  for eachPoint, Point in Points {
    Coord := StrSplit(Point, ",")
    NumPut('float', Coord[1], PointF, 8 * (A_Index - 1))
    NumPut('float', Coord[2], PointF, (8 * (A_Index - 1)) + 4)
  }
  return DllCall("gdiplus\GdipDrawLines", 'uptr', pGraphics, 'uptr', pPen, 'uptr', PointF.Ptr, "int", Points.Length)
}


; Function				Gdip_FillRectangle
; Description			This function uses a brush to fill a rectangle in the Graphics of a bitmap
;
; pGraphics				Pointer to the Graphics of a bitmap
; pBrush				Pointer to a brush
; x						x-coordinate of the top left of the rectangle
; y						y-coordinate of the top left of the rectangle
; w						width of the rectanlge
; h						height of the rectangle
;
; return				status enumeration. 0 = success
Gdip_FillRectangle(pGraphics, pBrush, x, y, w, h) {
  return DllCall("gdiplus\GdipFillRectangle"
    , 'uptr', pGraphics
    , 'uptr', pBrush
    , "float", x
    , "float", y
    , "float", w
    , "float", h)
}

; Function				Gdip_FillRoundedRectangle
; Description			This function uses a brush to fill a rounded rectangle in the Graphics of a bitmap
;
; pGraphics				Pointer to the Graphics of a bitmap
; pBrush				Pointer to a brush
; x						x-coordinate of the top left of the rounded rectangle
; y						y-coordinate of the top left of the rounded rectangle
; w						width of the rectanlge
; h						height of the rectangle
; r						radius of the rounded corners
;
; return				status enumeration. 0 = success

Gdip_FillRoundedRectangle(pGraphics, pBrush, x, y, w, h, r) {
  Region := Gdip_GetClipRegion(pGraphics)
  Gdip_SetClipRect(pGraphics, x - r, y - r, 2 * r, 2 * r, 4)
  Gdip_SetClipRect(pGraphics, x + w - r, y - r, 2 * r, 2 * r, 4)
  Gdip_SetClipRect(pGraphics, x - r, y + h - r, 2 * r, 2 * r, 4)
  Gdip_SetClipRect(pGraphics, x + w - r, y + h - r, 2 * r, 2 * r, 4)
  _E := Gdip_FillRectangle(pGraphics, pBrush, x, y, w, h)
  Gdip_SetClipRegion(pGraphics, Region, 0)
  Gdip_SetClipRect(pGraphics, x - (2 * r), y + r, w + (4 * r), h - (2 * r), 4)
  Gdip_SetClipRect(pGraphics, x + r, y - (2 * r), w - (2 * r), h + (4 * r), 4)
  Gdip_FillEllipse(pGraphics, pBrush, x, y, 2 * r, 2 * r)
  Gdip_FillEllipse(pGraphics, pBrush, x + w - (2 * r), y, 2 * r, 2 * r)
  Gdip_FillEllipse(pGraphics, pBrush, x, y + h - (2 * r), 2 * r, 2 * r)
  Gdip_FillEllipse(pGraphics, pBrush, x + w - (2 * r), y + h - (2 * r), 2 * r, 2 * r)
  Gdip_SetClipRegion(pGraphics, Region, 0)
  Gdip_DeleteRegion(Region)
  return _E
}

; Function				Gdip_FillPolygon
; Description			This function uses a brush to fill a polygon in the Graphics of a bitmap
;
; pGraphics				Pointer to the Graphics of a bitmap
; pBrush				Pointer to a brush
; Points				the coordinates of all the points passed as x1,y1|x2,y2|x3,y3.....
;
; return				status enumeration. 0 = success
;
; notes					Alternate will fill the polygon as a whole, wheras winding will fill each new "segment"
; Alternate 			= 0
; Winding 				= 1
Gdip_FillPolygon(pGraphics, pBrush, Points, FillMode := 0) {
  Points := StrSplit(Points, "|")
  PointF := Buffer(8 * Points.Length)
  For eachPoint, Point in Points {
    Coord := StrSplit(Point, ",")
    NumPut('float', Coord[1], PointF, 8 * (A_Index - 1))
    NumPut('float', Coord[2], PointF, (8 * (A_Index - 1)) + 4)
  }
  return DllCall("gdiplus\GdipFillPolygon", 'uptr', pGraphics, 'uptr', pBrush, 'uptr', PointF.Ptr, "int", Points.Length, "int", FillMode)
}

; Function				Gdip_FillPie
; Description			This function uses a brush to fill a pie in the Graphics of a bitmap
;
; pGraphics				Pointer to the Graphics of a bitmap
; pBrush				Pointer to a brush
; x						x-coordinate of the top left of the pie
; y						y-coordinate of the top left of the pie
; w						width of the pie
; h						height of the pie
; StartAngle			specifies the angle between the x-axis and the starting point of the pie
; SweepAngle			specifies the angle between the starting and ending points of the pie
;
; return				status enumeration. 0 = success
Gdip_FillPie(pGraphics, pBrush, x, y, w, h, StartAngle, SweepAngle) {
  return DllCall("gdiplus\GdipFillPie"
    , 'uptr', pGraphics
    , 'uptr', pBrush
    , "float", x
    , "float", y
    , "float", w
    , "float", h
    , "float", StartAngle
    , "float", SweepAngle)
}

; Function				Gdip_FillEllipse
; Description			This function uses a brush to fill an ellipse in the Graphics of a bitmap
;
; pGraphics				Pointer to the Graphics of a bitmap
; pBrush				Pointer to a brush
; x						x-coordinate of the top left of the ellipse
; y						y-coordinate of the top left of the ellipse
; w						width of the ellipse
; h						height of the ellipse
;
; return				status enumeration. 0 = success
Gdip_FillEllipse(pGraphics, pBrush, x, y, w, h) =>
  DllCall("gdiplus\GdipFillEllipse", 'uptr', pGraphics, 'uptr', pBrush, "float", x, "float", y, "float", w, "float", h)

; Function				Gdip_FillRegion
; Description			This function uses a brush to fill a region in the Graphics of a bitmap
;
; pGraphics				Pointer to the Graphics of a bitmap
; pBrush				Pointer to a brush
; Region				Pointer to a Region
;
; return				status enumeration. 0 = success
;
; notes					You can create a region Gdip_CreateRegion() and then add to this
Gdip_FillRegion(pGraphics, pBrush, Region) {
  return DllCall("gdiplus\GdipFillRegion", 'uptr', pGraphics, 'uptr', pBrush, 'uptr', Region)
}

; Function				Gdip_FillPath
; Description			This function uses a brush to fill a path in the Graphics of a bitmap
;
; pGraphics				Pointer to the Graphics of a bitmap
; pBrush				Pointer to a brush
; Region				Pointer to a Path
;
; return				status enumeration. 0 = success
Gdip_FillPath(pGraphics, pBrush, pPath) {
  return DllCall("gdiplus\GdipFillPath", 'uptr', pGraphics, 'uptr', pBrush, 'uptr', pPath)
}

; Function				Gdip_DrawImagePointsRect
; Description			This function draws a bitmap into the Graphics of another bitmap and skews it
;
; pGraphics				Pointer to the Graphics of a bitmap
; pBitmap				Pointer to a bitmap to be drawn
; Points				Points passed as x1,y1|x2,y2|x3,y3 (3 points: top left, top right, bottom left) describing the drawing of the bitmap
; sx					x-coordinate of source upper-left corner
; sy					y-coordinate of source upper-left corner
; sw					width of source rectangle
; sh					height of source rectangle
; Matrix				a matrix used to alter image attributes when drawing
;
; return				status enumeration. 0 = success
;
; notes					if sx,sy,sw,sh are missed then the entire source bitmap will be used
;						Matrix can be omitted to just draw with no alteration to ARGB
;						Matrix may be passed as a digit from 0 - 1 to change just transparency
;						Matrix can be passed as a matrix with any delimiter
Gdip_DrawImagePointsRect(pGraphics, pBitmap, Points, sx := "", sy := "", sw := "", sh := "", Matrix := 1) {
  Points := StrSplit(Points, "|")
  PointF := Buffer(8 * Points.Length)
  For eachPoint, Point in Points {
    Coord := StrSplit(Point, ",")
    NumPut('float', Coord[1], PointF, 8 * (A_Index - 1))
    NumPut('float', Coord[2], PointF, (8 * (A_Index - 1)) + 4)
  }
  if !IsNumber(Matrix)
    ImageAttr := Gdip_SetImageAttributesColorMatrix(Matrix)
  else if (Matrix != 1)
    ImageAttr := Gdip_SetImageAttributesColorMatrix("1|0|0|0|0|0|1|0|0|0|0|0|1|0|0|0|0|0|" Matrix "|0|0|0|0|0|1")
  if (sx = "" && sy = "" && sw = "" && sh = "") {
    sx := 0, sy := 0
    sw := Gdip_GetImageWidth(pBitmap)
    sh := Gdip_GetImageHeight(pBitmap)
  }
  _E := DllCall("gdiplus\GdipDrawImagePointsRect"
    , 'uptr', pGraphics
    , 'uptr', pBitmap
    , 'uptr', PointF.Ptr
    , "int", Points.Length
    , "float", sx
    , "float", sy
    , "float", sw
    , "float", sh
    , "int", 2
    , 'uptr', ImageAttr
    , 'uptr', 0
    , 'uptr', 0)
  if ImageAttr
    Gdip_DisposeImageAttributes(ImageAttr)
  return _E
}

; Function				Gdip_DrawImage
; Description			This function draws a bitmap into the Graphics of another bitmap
;
; pGraphics				Pointer to the Graphics of a bitmap
; pBitmap				Pointer to a bitmap to be drawn
; dx					x-coord of destination upper-left corner
; dy					y-coord of destination upper-left corner
; dw					width of destination image
; dh					height of destination image
; sx					x-coordinate of source upper-left corner
; sy					y-coordinate of source upper-left corner
; sw					width of source image
; sh					height of source image
; Matrix				a matrix used to alter image attributes when drawing
;
; return				status enumeration. 0 = success
;
; notes					if sx,sy,sw,sh are missed then the entire source bitmap will be used
;						Gdip_DrawImage performs faster
;						Matrix can be omitted to just draw with no alteration to ARGB
;						Matrix may be passed as a digit from 0 - 1 to change just transparency
;						Matrix can be passed as a matrix with any delimiter. For example:
;						MatrixBright=
;						(
;						1.5		|0		|0		|0		|0
;						0		|1.5	|0		|0		|0
;						0		|0		|1.5	|0		|0
;						0		|0		|0		|1		|0
;						0.05	|0.05	|0.05	|0		|1
;						)
;
; notes					MatrixBright = 1.5|0|0|0|0|0|1.5|0|0|0|0|0|1.5|0|0|0|0|0|1|0|0.05|0.05|0.05|0|1
;						MatrixGreyScale = 0.299|0.299|0.299|0|0|0.587|0.587|0.587|0|0|0.114|0.114|0.114|0|0|0|0|0|1|0|0|0|0|0|1
;						MatrixNegative = -1|0|0|0|0|0|-1|0|0|0|0|0|-1|0|0|0|0|0|1|0|1|1|1|0|1
Gdip_DrawImage(pGraphics, pBitmap, dx := "", dy := "", dw := "", dh := "", sx := "", sy := "", sw := "", sh := "", Matrix := 1) {
  ImageAttr := ''
  if !IsNumber(Matrix)
    ImageAttr := Gdip_SetImageAttributesColorMatrix(Matrix)
  else if (Matrix != 1)
    ImageAttr := Gdip_SetImageAttributesColorMatrix("1|0|0|0|0|0|1|0|0|0|0|0|1|0|0|0|0|0|" Matrix "|0|0|0|0|0|1")
  if (sx = "" && sy = "" && sw = "" && sh = "") {
    if (dx = "" && dy = "" && dw = "" && dh = "") {
      sx := dx := 0, sy := dy := 0
      sw := dw := Gdip_GetImageWidth(pBitmap)
      sh := dh := Gdip_GetImageHeight(pBitmap)
    } else {
      sx := sy := 0
      sw := Gdip_GetImageWidth(pBitmap)
      sh := Gdip_GetImageHeight(pBitmap)
    }
  }
  _E := DllCall("gdiplus\GdipDrawImageRectRect"
    , 'uptr', pGraphics, 'uptr', pBitmap
    , "float", dx, "float", dy, "float", dw, "float", dh
    , "float", sx, "float", sy, "float", sw, "float", sh
    , "int", 2, 'uptr', ImageAttr ? ImageAttr : 0
    , 'uptr', 0, 'uptr', 0)
  if ImageAttr
    Gdip_DisposeImageAttributes(ImageAttr)
  return _E
}


; Function				Gdip_SetImageAttributesColorMatrix
; Description			This function creates an image matrix ready for drawing
;
; Matrix				a matrix used to alter image attributes when drawing
;						passed with any delimeter
;
; return				returns an image matrix on sucess or 0 if it fails
;
; notes					MatrixBright = 1.5|0|0|0|0|0|1.5|0|0|0|0|0|1.5|0|0|0|0|0|1|0|0.05|0.05|0.05|0|1
;						MatrixGreyScale = 0.299|0.299|0.299|0|0|0.587|0.587|0.587|0|0|0.114|0.114|0.114|0|0|0|0|0|1|0|0|0|0|0|1
;						MatrixNegative = -1|0|0|0|0|0|-1|0|0|0|0|0|-1|0|0|0|0|0|1|0|1|1|1|0|1
Gdip_SetImageAttributesColorMatrix(Matrix) {
  ColourMatrix := Buffer(100, 0)
  Matrix := RegExReplace(RegExReplace(Matrix, "^[^\d-\.]+([\d\.])", "$1", , 1), "[^\d-\.]+", "|")
  Matrix := StrSplit(Matrix, "|")
  Loop 25 {
    M := (Matrix[A_Index] != "") ? Matrix[A_Index] : Mod(A_Index - 1, 6) ? 0 : 1
    NumPut('float', M, ColourMatrix, (A_Index - 1) * 4)
  }
  DllCall("gdiplus\GdipCreateImageAttributes", A_PtrSize ? "UPtr*" : "uint*", &ImageAttr := 0)
  DllCall("gdiplus\GdipSetImageAttributesColorMatrix", 'uptr', ImageAttr, "int", 1, "int", 1, 'uptr', ColourMatrix.Ptr, 'uptr', 0, "int", 0)
  return ImageAttr
}


; Function				Gdip_GraphicsFromImage
; Description			This function gets the graphics for a bitmap used for drawing functions
;
; pBitmap				Pointer to a bitmap to get the pointer to its graphics
;
; return				returns a pointer to the graphics of a bitmap
;
; notes					a bitmap can be drawn into the graphics of another bitmap
Gdip_GraphicsFromImage(pBitmap) {
  DllCall("gdiplus\GdipGetImageGraphicsContext"
    , 'uptr', pBitmap, "UPtr*", &pGraphics := 0)
  return pGraphics
}

; Function				Gdip_GraphicsFromHDC
; Description			This function gets the graphics from the handle to a device context
;
; hdc					This is the handle to the device context
;
; return				returns a pointer to the graphics of a bitmap
;
; notes					You can draw a bitmap into the graphics of another bitmap
Gdip_GraphicsFromHDC(hdc) {
  DllCall("gdiplus\GdipCreateFromHDC", 'ptr', hdc, 'uptr*', &pGraphics := 0)
  return pGraphics
}

; Function				Gdip_GetDC
; Description			This function gets the device context of the passed Graphics
;
; hdc					This is the handle to the device context
;
; return				returns the device context for the graphics of a bitmap
Gdip_GetDC(pGraphics) {
  DllCall("gdiplus\GdipGetDC", "UPtr", pGraphics, "UPtr*", &hdc := 0)
  return hdc
}

; Function				Gdip_ReleaseDC
; Description			This function releases a device context from use for further use
;
; pGraphics				Pointer to the graphics of a bitmap
; hdc					This is the handle to the device context
;
; return				status enumeration. 0 = success
Gdip_ReleaseDC(pGraphics, hdc) => DllCall("gdiplus\GdipReleaseDC", 'uptr', pGraphics, 'uptr', hdc)


; Function				Gdip_GraphicsClear
; Description			Clears the graphics of a bitmap ready for further drawing
;
; pGraphics				Pointer to the graphics of a bitmap
; ARGB					The colour to clear the graphics to
;
; return				status enumeration. 0 = success
;
; notes					By default this will make the background invisible
;						Using clipping regions you can clear a particular area on the graphics rather than clearing the entire graphics
Gdip_GraphicsClear(pGraphics, ARGB := 0x00ffffff) => DllCall("gdiplus\GdipGraphicsClear", 'uptr', pGraphics, "int", ARGB)

; Function				Gdip_BlurBitmap
; Description			Gives a pointer to a blurred bitmap from a pointer to a bitmap
;
; pBitmap				Pointer to a bitmap to be blurred
; Blur					The Amount to blur a bitmap by from 1 (least blur) to 100 (most blur)
;
; return				If the function succeeds, the return value is a pointer to the new blurred bitmap
;						-1 = The blur parameter is outside the range 1-100
;
; notes					This function will not dispose of the original bitmap
Gdip_BlurBitmap(pBitmap, Blur) {
  if (Blur > 100) || (Blur < 1)
    return -1
  sWidth := Gdip_GetImageWidth(pBitmap), sHeight := Gdip_GetImageHeight(pBitmap)
  dWidth := sWidth // Blur, dHeight := sHeight // Blur
  pBitmap1 := Gdip_CreateBitmap(dWidth, dHeight)
  G1 := Gdip_GraphicsFromImage(pBitmap1)
  Gdip_SetInterpolationMode(G1, 7)
  Gdip_DrawImage(G1, pBitmap, 0, 0, dWidth, dHeight, 0, 0, sWidth, sHeight)
  Gdip_DeleteGraphics(G1)
  pBitmap2 := Gdip_CreateBitmap(sWidth, sHeight)
  G2 := Gdip_GraphicsFromImage(pBitmap2)
  Gdip_SetInterpolationMode(G2, 7)
  Gdip_DrawImage(G2, pBitmap1, 0, 0, sWidth, sHeight, 0, 0, dWidth, dHeight)
  Gdip_DeleteGraphics(G2)
  Gdip_DisposeImage(pBitmap1)
  return pBitmap2
}


; Function:				Gdip_SaveBitmapToFile
; Description:			Saves a bitmap to a file in any supported format onto disk
;
; pBitmap				Pointer to a bitmap
; sOutput				The name of the file that the bitmap will be saved to. Supported extensions are: .BMP,.DIB,.RLE,.JPG,.JPEG,.JPE,.JFIF,.GIF,.TIF,.TIFF,.PNG
; Quality				If saving as jpg (.JPG,.JPEG,.JPE,.JFIF) then quality can be 1-100 with default at maximum quality
;
; return				If the function succeeds, the return value is zero, otherwise:
;						-1 = Extension supplied is not a supported file format
;						-2 = Could not get a list of encoders on system
;						-3 = Could not find matching encoder for specified file format
;						-4 = Could not get WideChar name of output file
;						-5 = Could not save file to disk
;
; notes					This function will use the extension supplied from the sOutput parameter to determine the output format
; @OK
Gdip_SaveBitmapToFile(pBitmap, sOutput, Quality := 75) {
  _p := 0
  SplitPath sOutput, , , &Extension
  if !RegExMatch(Extension, "^(?i:BMP|DIB|RLE|JPG|JPEG|JPE|JFIF|GIF|TIF|TIFF|PNG)$")
    return -1
  Extension := "." Extension
  DllCall("gdiplus\GdipGetImageEncodersSize", "uint*", &nCount := 0, "uint*", &nSize := 0)
  ci := Buffer(nSize)
  DllCall("gdiplus\GdipGetImageEncoders", "uint", nCount, "uint", nSize, 'uptr', ci.Ptr)
  if !(nCount && nSize)
    return -2

  Loop nCount {
    sString := StrGet(NumGet(ci, (idx := (48 + 7 * A_PtrSize) * (A_Index - 1)) + 32 + 3 * A_PtrSize, 'int'), "UTF-16")
    if !InStr(sString, "*" Extension)
      continue
    pCodec := ci.Ptr + idx
    break
  }
  if !pCodec
    return -3
  if (Quality != 75) {
    Quality := (Quality < 0) ? 0 : (Quality >= 100) ? 100 : Quality
    if RegExMatch(Extension, "^\.(?i:JPG|JPEG|JPE|JFIF)$") {
      DllCall("gdiplus\GdipGetEncoderParameterListSize", 'uptr', pCodec, "uint*", &nSize)
      EncoderParameters := Buffer(nSize, 0)
      DllCall("gdiplus\GdipGetEncoderParameterList", 'uptr', pCodec, "uint", nSize, 'uptr', EncoderParameters.Ptr)
      nCount := NumGet(EncoderParameters, "UInt")
      Loop nCount {
        elem := (24 + (A_PtrSize ? A_PtrSize : 4)) * (A_Index - 1) + 4 + (pad := A_PtrSize = 8 ? 4 : 0)
        if (NumGet(EncoderParameters, elem + 16, "UInt") = 1) && (NumGet(EncoderParameters, elem + 20, "UInt") = 6) {
          _p := elem + EncoderParameters.Ptr - pad - 4
          NumPut('uint', 1, EncoderParameters, 40)
          NumPut('uint', 4, EncoderParameters, 60)
          NumPut('uint', Quality, EncoderParameters, 64)
          break
        }
      }
    }
  }
  _E := DllCall("gdiplus\GdipSaveImageToFile", 'uptr', pBitmap, 'uptr', strptr(sOutput), 'uptr', pCodec, "uint", _p ? _p : 0)
  return _E ? -5 : 0
}

; Function				Gdip_GetPixel
; Description			Gets the ARGB of a pixel in a bitmap
;
; pBitmap				Pointer to a bitmap
; x						x-coordinate of the pixel
; y						y-coordinate of the pixel
;
; return				Returns the ARGB value of the pixel
Gdip_GetPixel(pBitmap, x, y) {
  DllCall("gdiplus\GdipBitmapGetPixel", 'uptr', pBitmap, "int", x, "int", y, "uint*", &ARGB := 0)
  return ARGB
}


; Function				Gdip_SetPixel
; Description			Sets the ARGB of a pixel in a bitmap
;
; pBitmap				Pointer to a bitmap
; x						x-coordinate of the pixel
; y						y-coordinate of the pixel
;
; return				status enumeration. 0 = success
Gdip_SetPixel(pBitmap, x, y, ARGB) => DllCall("gdiplus\GdipBitmapSetPixel", 'uptr', pBitmap, "int", x, "int", y, "int", ARGB)


; Function				Gdip_GetImageWidth
; Description			Gives the width of a bitmap
;
; pBitmap				Pointer to a bitmap
;
; return				Returns the width in pixels of the supplied bitmap
Gdip_GetImageWidth(pBitmap) {
  DllCall("gdiplus\GdipGetImageWidth", 'uptr', pBitmap, "uint*", &Width := 0)
  return Width
}


; Function				Gdip_GetImageHeight
; Description			Gives the height of a bitmap
;
; pBitmap				Pointer to a bitmap
;
; return				Returns the height in pixels of the supplied bitmap
Gdip_GetImageHeight(pBitmap) {
  DllCall("gdiplus\GdipGetImageHeight", 'uptr', pBitmap, "uint*", &Height := 0)
  return Height
}

; Function				Gdip_GetDimensions
; Description			Gives the width and height of a bitmap
;
; pBitmap				Pointer to a bitmap
; Width					ByRef variable. This variable will be set to the width of the bitmap
; Height				ByRef variable. This variable will be set to the height of the bitmap
;
; return				No return value
;						Gdip_GetDimensions(pBitmap, ThisWidth, ThisHeight) will set ThisWidth to the width and ThisHeight to the height
Gdip_GetDimensions(pBitmap, &Width, &Height) => Gdip_GetImageDimensions(pBitmap, &Width, &Height)

Gdip_GetImageDimensions(pBitmap, &Width, &Height) {
  DllCall("gdiplus\GdipGetImageWidth", 'uptr', pBitmap, "uint*", &Width := 0)
  DllCall("gdiplus\GdipGetImageHeight", 'uptr', pBitmap, "uint*", &Height := 0)
}

Gdip_GetImagePixelFormat(pBitmap) {
  DllCall("gdiplus\GdipGetImagePixelFormat", 'uptr', pBitmap, 'uptr*', &Format := 0)
  return Format
}


; Function				Gdip_GetDpiX
; Description			Gives the horizontal dots per inch of the graphics of a bitmap
;
; pBitmap				Pointer to a bitmap
; Width					ByRef variable. This variable will be set to the width of the bitmap
; Height				ByRef variable. This variable will be set to the height of the bitmap
;
; return				No return value
;						Gdip_GetDimensions(pBitmap, ThisWidth, ThisHeight) will set ThisWidth to the width and ThisHeight to the height
Gdip_GetDpiX(pGraphics) {
  DllCall("gdiplus\GdipGetDpiX", 'uptr', pGraphics, "float*", &dpix := 0)
  return Round(dpix)
}
Gdip_GetDpiY(pGraphics) {
  DllCall("gdiplus\GdipGetDpiY", 'uptr', pGraphics, "float*", &dpiy := 0)
  return Round(dpiy)
}
Gdip_GetImageHorizontalResolution(pBitmap) {
  DllCall("gdiplus\GdipGetImageHorizontalResolution", 'uptr', pBitmap, "float*", &dpix := 0)
  return Round(dpix)
}
Gdip_GetImageVerticalResolution(pBitmap) {
  DllCall("gdiplus\GdipGetImageVerticalResolution", 'uptr', pBitmap, "float*", &dpiy := 0)
  return Round(dpiy)
}
Gdip_BitmapSetResolution(pBitmap, dpix, dpiy) => DllCall("gdiplus\GdipBitmapSetResolution", 'uptr', pBitmap, "float", dpix, "float", dpiy)

Gdip_CreateBitmapFromFile(sFile, IconNumber := 1, IconSize := "") {
  SplitPath sFile, , , &Extension
  if RegExMatch(Extension, "^(?i:exe|dll)$") {
    Sizes := IconSize ? IconSize : 256 "|" 128 "|" 64 "|" 48 "|" 32 "|" 16
    BufSize := 16 + (2 * (A_PtrSize ? A_PtrSize : 4))
    buf := Buffer(BufSize, 0)
    For eachSize, Size in StrSplit(Sizes, "|") {
      DllCall("PrivateExtractIcons", "str", sFile, "int", IconNumber - 1, "int", Size, "int", Size, 'uptr*', &hIcon := 0, 'uptr*', 0, "uint", 1, "uint", 0)
      if !hIcon
        continue
      if !DllCall("GetIconInfo", 'uptr', hIcon, 'uptr', buf.Ptr) {
        DestroyIcon(hIcon)
        continue
      }
      hbmMask := NumGet(buf, 12 + ((A_PtrSize ? A_PtrSize : 4) - 4))
      hbmColor := NumGet(buf, 12 + ((A_PtrSize ? A_PtrSize : 4) - 4) + (A_PtrSize ? A_PtrSize : 4))
      if !(hbmColor && DllCall("GetObject", 'uptr', hbmColor, "int", BufSize, 'uptr', buf.Ptr)) {
        DestroyIcon(hIcon)
        continue
      }
      break
    }
    if !hIcon
      return -1
    Width := NumGet(buf, 4, "int"), Height := NumGet(buf, 8, "int")
    hbm := CreateDIBSection(Width, -Height), hdc := CreateCompatibleDC(), obm := SelectObject(hdc, hbm)
    if !DllCall("DrawIconEx", 'uptr', hdc, "int", 0, "int", 0, 'uptr', hIcon, "uint", Width, "uint", Height, "uint", 0, 'uptr', 0, "uint", 3) {
      DestroyIcon(hIcon)
      return -2
    }
    dib := Buffer(104)
    DllCall("GetObject", 'uptr', hbm, "int", A_PtrSize = 8 ? 104 : 84, 'uptr', dib.Ptr)
    Stride := NumGet(dib, 12, "Int"), Bits := NumGet(dib, 20 + (A_PtrSize = 8 ? 4 : 0))
    DllCall("gdiplus\GdipCreateBitmapFromScan0", "int", Width, "int", Height, "int", Stride, "int", 0x26200A, 'uptr', Bits, 'uptr*', &pBitmapOld := 0)
    pBitmap := Gdip_CreateBitmap(Width, Height)
    _G := Gdip_GraphicsFromImage(pBitmap), Gdip_DrawImage(_G, pBitmapOld, 0, 0, Width, Height, 0, 0, Width, Height)
    SelectObject(hdc, obm), DeleteObject(hbm), DeleteDC(hdc)
    Gdip_DeleteGraphics(_G), Gdip_DisposeImage(pBitmapOld)
    DestroyIcon(hIcon)
  } else {
    DllCall("gdiplus\GdipCreateBitmapFromFile", 'uptr', StrPtr(sFile), 'uptr*', &pBitmap := 0)
  }
  return pBitmap
}


Gdip_CreateBitmapFromHBITMAP(hBitmap, Palette := 0) {
  DllCall("gdiplus\GdipCreateBitmapFromHBITMAP"
    , 'uptr', hBitmap, 'uptr', Palette, 'uptr*', &pBitmap := 0)
  return pBitmap
}

Gdip_CreateHBITMAPFromBitmap(pBitmap, Background := 0xffffffff) {
  DllCall("gdiplus\GdipCreateHBITMAPFromBitmap", 'uptr', pBitmap, 'uptr*', &hbm := 0, "int", Background)
  return hbm
}
Gdip_CreateBitmapFromHICON(hIcon) {
  DllCall("gdiplus\GdipCreateBitmapFromHICON", 'uptr', hIcon, 'uptr*', &pBitmap := 0)
  return pBitmap
}
Gdip_CreateHICONFromBitmap(pBitmap) {
  DllCall("gdiplus\GdipCreateHICONFromBitmap", 'uptr', pBitmap, 'uptr*', &hIcon := 0)
  return hIcon
}
Gdip_CreateBitmap(Width, Height, Format := 0x26200A) {
  DllCall("gdiplus\GdipCreateBitmapFromScan0", "int", Width, "int", Height, "int", 0, "int", Format, 'uptr', 0, 'uptr*', &pBitmap := 0)
  Return pBitmap
}
Gdip_CreateBitmapFromClipboard() {
  if !DllCall("OpenClipboard", 'uptr', 0)
    return -1
  if !DllCall("IsClipboardFormatAvailable", "uint", 8)
    return -2
  if !hBitmap := DllCall("GetClipboardData", "uint", 2, 'uptr')
    return -3
  if !pBitmap := Gdip_CreateBitmapFromHBITMAP(hBitmap)
    return -4
  if !DllCall("CloseClipboard")
    return -5
  DeleteObject(hBitmap)
  return pBitmap
}

Gdip_SetBitmapToClipboard(pBitmap) {
  off1 := A_PtrSize = 8 ? 52 : 44, off2 := A_PtrSize = 8 ? 32 : 24
  hBitmap := Gdip_CreateHBITMAPFromBitmap(pBitmap)
  DllCall("GetObject", 'uptr', hBitmap, "int", (oi := Buffer(A_PtrSize = 8 ? 104 : 84, 0)).Ptr, 'uptr', oi.Ptr)
  hdib := DllCall("GlobalAlloc", "uint", 2, 'uptr', 40 + NumGet(oi, off1, "UInt"), 'uptr')
  pdib := DllCall("GlobalLock", 'uptr', hdib, 'uptr')
  DllCall("RtlMoveMemory", 'uptr', pdib, 'uptr', oi.Ptr + off2, 'uptr', 40)
  DllCall("RtlMoveMemory", 'uptr', pdib + 40, 'uptr', NumGet(oi, off2 - A_PtrSize, 'uptr'), 'uptr', NumGet(oi, off1, "UInt"))
  DllCall("GlobalUnlock", 'uptr', hdib)
  DllCall("DeleteObject", 'uptr', hBitmap)
  DllCall("OpenClipboard", 'uptr', 0)
  DllCall("EmptyClipboard")
  DllCall("SetClipboardData", "uint", 8, 'uptr', hdib)
  DllCall("CloseClipboard")
}

Gdip_CloneBitmapArea(pBitmap, x, y, w, h, Format := 0x26200A) {
  DllCall("gdiplus\GdipCloneBitmapArea"
    , "float", x
    , "float", y
    , "float", w
    , "float", h
    , "int", Format
    , 'uptr', pBitmap
    , 'uptr*', &pBitmapDest := 0)
  return pBitmapDest
}

; Create resources

Gdip_CreatePen(ARGB, w) {
  DllCall("gdiplus\GdipCreatePen1"
    , "uint", ARGB, "float", w
    , "int", 2, "uptr*", &pPen := 0)
  return pPen
}

Gdip_BrushCreateSolid(ARGB := 0xff000000) {
  DllCall("gdiplus\GdipCreateSolidFill"
    , "UInt", ARGB, "uptr*", &pBrush := 0)
  return pBrush
}

Gdip_CreatePenFromBrush(pBrush, w) {
  DllCall("gdiplus\GdipCreatePen2"
    , 'uptr', pBrush
    , "float", w, "int", 2, 'uptr*', &pPen := 0)
  return pPen
}


; HatchStyleHorizontal = 0
; HatchStyleVertical = 1
; HatchStyleForwardDiagonal = 2
; HatchStyleBackwardDiagonal = 3
; HatchStyleCross = 4
; HatchStyleDiagonalCross = 5
; HatchStyle05Percent = 6
; HatchStyle10Percent = 7
; HatchStyle20Percent = 8
; HatchStyle25Percent = 9
; HatchStyle30Percent = 10
; HatchStyle40Percent = 11
; HatchStyle50Percent = 12
; HatchStyle60Percent = 13
; HatchStyle70Percent = 14
; HatchStyle75Percent = 15
; HatchStyle80Percent = 16
; HatchStyle90Percent = 17
; HatchStyleLightDownwardDiagonal = 18
; HatchStyleLightUpwardDiagonal = 19
; HatchStyleDarkDownwardDiagonal = 20
; HatchStyleDarkUpwardDiagonal = 21
; HatchStyleWideDownwardDiagonal = 22
; HatchStyleWideUpwardDiagonal = 23
; HatchStyleLightVertical = 24
; HatchStyleLightHorizontal = 25
; HatchStyleNarrowVertical = 26
; HatchStyleNarrowHorizontal = 27
; HatchStyleDarkVertical = 28
; HatchStyleDarkHorizontal = 29
; HatchStyleDashedDownwardDiagonal = 30
; HatchStyleDashedUpwardDiagonal = 31
; HatchStyleDashedHorizontal = 32
; HatchStyleDashedVertical = 33
; HatchStyleSmallConfetti = 34
; HatchStyleLargeConfetti = 35
; HatchStyleZigZag = 36
; HatchStyleWave = 37
; HatchStyleDiagonalBrick = 38
; HatchStyleHorizontalBrick = 39
; HatchStyleWeave = 40
; HatchStylePlaid = 41
; HatchStyleDivot = 42
; HatchStyleDottedGrid = 43
; HatchStyleDottedDiamond = 44
; HatchStyleShingle = 45
; HatchStyleTrellis = 46
; HatchStyleSphere = 47
; HatchStyleSmallGrid = 48
; HatchStyleSmallCheckerBoard = 49
; HatchStyleLargeCheckerBoard = 50
; HatchStyleOutlinedDiamond = 51
; HatchStyleSolidDiamond = 52
; HatchStyleTotal = 53
Gdip_BrushCreateHatch(ARGBfront, ARGBback, HatchStyle := 0) {
  DllCall("gdiplus\GdipCreateHatchBrush"
    , "int", HatchStyle, "UInt", ARGBfront
    , "UInt", ARGBback, 'uptr*', &pBrush := 0)
  return pBrush
}

Gdip_CreateTextureBrush(pBitmap, WrapMode := 1, x := 0, y := 0, w := "", h := "") {
  if !(w && h) {
    DllCall("gdiplus\GdipCreateTexture", 'uptr', pBitmap, "int", WrapMode, 'uptr*', &pBrush := 0)
  } else DllCall("gdiplus\GdipCreateTexture2"
    , 'uptr', pBitmap, "int", WrapMode
    , "float", x, "float", y, "float", w, "float", h, 'uptr*', &pBrush := 0)
  return pBrush
}

; WrapModeTile = 0
; WrapModeTileFlipX = 1
; WrapModeTileFlipY = 2
; WrapModeTileFlipXY = 3
; WrapModeClamp = 4
Gdip_CreateLineBrush(x1, y1, x2, y2, ARGB1, ARGB2, WrapMode := 1) {
  CreatePointF(&PointF1, x1, y1)
  CreatePointF(&PointF2, x2, y2)
  DllCall("gdiplus\GdipCreateLineBrush"
    , 'uptr', PointF1.Ptr, 'uptr', PointF2.Ptr
    , "Uint", ARGB1, "Uint", ARGB2, "int", WrapMode
    , 'uptr*', &LGpBrush := 0)
  return LGpBrush
}

; LinearGradientModeHorizontal = 0
; LinearGradientModeVertical = 1
; LinearGradientModeForwardDiagonal = 2
; LinearGradientModeBackwardDiagonal = 3
Gdip_CreateLineBrushFromRect(x, y, w, h, ARGB1, ARGB2, LinearGradientMode := 1, WrapMode := 1) {
  CreateRectF(&RectF, x, y, w, h)
  DllCall("gdiplus\GdipCreateLineBrushFromRect"
    , 'uptr', RectF.Ptr
    , "int", ARGB1, "int", ARGB2, "int", LinearGradientMode
    , "int", WrapMode, 'uptr*', &LGpBrush := 0)
  return LGpBrush
}

Gdip_CloneBrush(pBrush) {
  DllCall("gdiplus\GdipCloneBrush", 'uptr', StrPtr(pBrush), 'uptr*', &pBrushClone := 0)
  return pBrushClone
}

Gdip_DeleteGraphics(pGraphics) => DllCall("gdiplus\GdipDeleteGraphics", "UPtr", pGraphics)
Gdip_DeletePen(pPen) => DllCall("gdiplus\GdipDeletePen", 'uptr', pPen)
Gdip_DeleteBrush(pBrush) => DllCall("gdiplus\GdipDeleteBrush", 'uptr', pBrush)
Gdip_DisposeImage(pBitmap) => DllCall("gdiplus\GdipDisposeImage", 'uptr', pBitmap)
Gdip_DisposeImageAttributes(ImageAttr) => DllCall("gdiplus\GdipDisposeImageAttributes", 'uptr', ImageAttr)
Gdip_DeleteFont(hFont) => DllCall("gdiplus\GdipDeleteFont", 'uptr', hFont)
Gdip_DeleteStringFormat(hFormat) => DllCall("gdiplus\GdipDeleteStringFormat", 'uptr', hFormat)
Gdip_DeleteFontFamily(hFamily) => DllCall("gdiplus\GdipDeleteFontFamily", 'uptr', hFamily)
Gdip_DeleteMatrix(Matrix) => DllCall("gdiplus\GdipDeleteMatrix", 'uptr', Matrix)

; Text functions

Gdip_TextToGraphics(pGraphics, Text, Options, Font := "Arial", Width := "", Height := "", Measure := 0) {
  IWidth := Width, IHeight := Height

  RegExMatch(Options, "i)X([\-\d\.]+)(p*)", &xpos)
  RegExMatch(Options, "i)Y([\-\d\.]+)(p*)", &ypos)
  RegExMatch(Options, "i)W([\-\d\.]+)(p*)", &Width)
  RegExMatch(Options, "i)H([\-\d\.]+)(p*)", &Height)
  RegExMatch(Options, "i)C(?!(entre|enter))([a-f\d]+)", &Colour)
  RegExMatch(Options, "i)Top|Up|Bottom|Down|vCentre|vCenter", &vPos)
  RegExMatch(Options, "i)NoWrap", &NoWrap)
  RegExMatch(Options, "i)R(\d)", &Rendering)
  RegExMatch(Options, "i)S(\d+)(p*)", &Size)

  PassBrush := 0
  if Colour && IsInteger(Colour[2]) ;&& !Gdip_DeleteBrush(Gdip_CloneBrush(Colour[2]))
    PassBrush := 1, pBrush := Colour[2]

  if !(IWidth && IHeight) && ((xpos && xpos[2]) || (ypos && ypos[2]) || (Width && Width[2]) || (Height && Height[2]) || (Size && Size[2]))
    return -1

  Style := 0, Styles := "Regular|Bold|Italic|BoldItalic|Underline|Strikeout"
  For eachStyle, valStyle in StrSplit(Styles, "|") {
    if RegExMatch(Options, "\b" valStyle)
      Style |= (valStyle != "StrikeOut") ? (A_Index - 1) : 8
  }

  Align := 0, Alignments := "Near|Left|Centre|Center|Far|Right"
  For eachAlignment, valAlignment in StrSplit(Alignments, "|") {
    if RegExMatch(Options, "\b" valAlignment)
      Align |= A_Index // 2	; 0|0|1|1|2|2
  }

  xpos := (xpos && (xpos[1] != "")) ? xpos[2] ? IWidth * (xpos[1] / 100) : xpos[1] : 0
  ypos := (ypos && (ypos[1] != "")) ? ypos[2] ? IHeight * (ypos[1] / 100) : ypos[1] : 0
  Width := (Width && Width[1]) ? Width[2] ? IWidth * (Width[1] / 100) : Width[1] : IWidth
  Height := (Height && Height[1]) ? Height[2] ? IHeight * (Height[1] / 100) : Height[1] : IHeight
  if !PassBrush
    Colour := "0x" (Colour && Colour[2] ? Colour[2] : "ff000000")
  Rendering := (Rendering && (Rendering[1] >= 0) && (Rendering[1] <= 5)) ? Rendering[1] : 4
  Size := (Size && (Size[1] > 0)) ? Size[2] ? IHeight * (Size[1] / 100) : Size[1] : 12

  hFamily := Gdip_FontFamilyCreate(Font)
  hFont := Gdip_FontCreate(hFamily, Size, Style)
  FormatStyle := NoWrap ? 0x4000 | 0x1000 : 0x4000
  hFormat := Gdip_StringFormatCreate(FormatStyle)
  pBrush := PassBrush ? pBrush : Gdip_BrushCreateSolid(Colour)
  if !(hFamily && hFont && hFormat && pBrush && pGraphics)
    return !pGraphics ? -2 : !hFamily ? -3 : !hFont ? -4 : !hFormat ? -5 : !pBrush ? -6 : 0

  CreateRectF(&RC, xpos, ypos, Width, Height)
  Gdip_SetStringFormatAlign(hFormat, Align)
  Gdip_SetTextRenderingHint(pGraphics, Rendering)
  ReturnRC := Gdip_MeasureString(pGraphics, Text, hFont, hFormat, &RC)

  if vPos
  {
    ReturnRC := StrSplit(ReturnRC, "|")

    if (vPos[0] = "vCentre") || (vPos[0] = "vCenter")
      ypos += (Height - ReturnRC[4]) // 2
    else if (vPos[0] = "Top") || (vPos[0] = "Up")
      ypos := 0
    else if (vPos[0] = "Bottom") || (vPos[0] = "Down")
      ypos := Height - ReturnRC[4]

    CreateRectF(&RC, xpos, ypos, Width, ReturnRC[4])
    ReturnRC := Gdip_MeasureString(pGraphics, Text, hFont, hFormat, &RC)
  }

  _E := ''
  if !Measure
    _E := Gdip_DrawString(pGraphics, Text, hFont, hFormat, pBrush, &RC)

  if !PassBrush
    Gdip_DeleteBrush(pBrush)
  Gdip_DeleteStringFormat(hFormat)
  Gdip_DeleteFont(hFont)
  Gdip_DeleteFontFamily(hFamily)
  return _E ? _E : ReturnRC
}

Gdip_DrawString(pGraphics, sString, hFont, hFormat, pBrush, &RectF) {
  return DllCall("gdiplus\GdipDrawString"
    , 'uptr', pGraphics
    , 'uptr', StrPtr(sString)
    , "int", -1
    , 'uptr', hFont
    , 'uptr', RectF.Ptr
    , 'uptr', hFormat
    , 'uptr', pBrush)
}

Gdip_MeasureString(pGraphics, sString, hFont, hFormat, &RectF) {
  RC := Buffer(16)
  DllCall("gdiplus\GdipMeasureString"
    , 'uptr', pGraphics
    , 'uptr', StrPtr(sString)
    , "int", -1
    , 'uptr', hFont
    , 'uptr', RectF.Ptr
    , 'uptr', hFormat
    , 'uptr', RC.Ptr
    , "uint*", &Chars := 0
    , "uint*", &Lines := 0)

  return RC ? NumGet(RC, 0, "float") "|" NumGet(RC, 4, "float") "|" NumGet(RC, 8, "float") "|" NumGet(RC, 12, "float") "|" Chars "|" Lines : 0
}

; Near = 0
; Center = 1
; Far = 2
Gdip_SetStringFormatAlign(hFormat, Align) =>
  DllCall("gdiplus\GdipSetStringFormatAlign", 'uptr', hFormat, "int", Align)

; StringFormatFlagsDirectionRightToLeft    = 0x00000001
; StringFormatFlagsDirectionVertical       = 0x00000002
; StringFormatFlagsNoFitBlackBox           = 0x00000004
; StringFormatFlagsDisplayFormatControl    = 0x00000020
; StringFormatFlagsNoFontFallback          = 0x00000400
; StringFormatFlagsMeasureTrailingSpaces   = 0x00000800
; StringFormatFlagsNoWrap                  = 0x00001000
; StringFormatFlagsLineLimit               = 0x00002000
; StringFormatFlagsNoClip                  = 0x00004000
Gdip_StringFormatCreate(Format := 0, Lang := 0) {
  DllCall("gdiplus\GdipCreateStringFormat", "int", Format, "int", Lang, 'uptr*', &hFormat := 0)
  return hFormat
}

; Regular = 0
; Bold = 1
; Italic = 2
; BoldItalic = 3
; Underline = 4
; Strikeout = 8
Gdip_FontCreate(hFamily, Size, Style := 0) {
  DllCall("gdiplus\GdipCreateFont"
    , 'uptr', hFamily
    , "float", Size, "int", Style, "int", 0, 'uptr*', &hFont := 0)
  return hFont
}

Gdip_FontFamilyCreate(Font) {
  DllCall("gdiplus\GdipCreateFontFamilyFromName"
    , 'uptr', StrPtr(Font)
    , "uint", 0
    , 'uptr*', &hFamily := 0)
  return hFamily
}

; Matrix functions

Gdip_CreateAffineMatrix(m11, m12, m21, m22, x, y) {
  DllCall("gdiplus\GdipCreateMatrix2"
    , "float", m11, "float", m12, "float", m21, "float", m22
    , "float", x, "float", y, 'uptr*', &Matrix := 0)
  return Matrix
}

Gdip_CreateMatrix() {
  DllCall("gdiplus\GdipCreateMatrix", 'uptr*', &Matrix := 0)
  return Matrix
}

; Alternate = 0
; Winding = 1
Gdip_CreatePath(BrushMode := 0) {
  DllCall("gdiplus\GdipCreatePath", "int", BrushMode, 'uptr*', &pPath := 0)
  return pPath
}

Gdip_AddPathEllipse(pPath, x, y, w, h) {
  return DllCall("gdiplus\GdipAddPathEllipse"
    , 'uptr', pPath, "float", x, "float", y, "float", w, "float", h)
}

Gdip_AddPathPolygon(pPath, Points) {
  Points := StrSplit(Points, "|")
  PointF := Buffer(8 * Points.Length)
  for eachPoint, Point in Points {
    Coord := StrSplit(Point, ",")
    NumPut("float", Coord[1], PointF, 8 * (A_Index - 1))
    NumPut('float', Coord[2], PointF, (8 * (A_Index - 1)) + 4)
  }
  return DllCall("gdiplus\GdipAddPathPolygon", 'uptr', pPath, 'uptr', PointF, "int", Points.Length)
}

Gdip_DeletePath(pPath) => DllCall("gdiplus\GdipDeletePath", 'uptr', pPath)

; Quality functions

; SystemDefault = 0
; SingleBitPerPixelGridFit = 1
; SingleBitPerPixel = 2
; AntiAliasGridFit = 3
; AntiAlias = 4
Gdip_SetTextRenderingHint(pGraphics, RenderingHint) =>
  DllCall("gdiplus\GdipSetTextRenderingHint"
    , 'uptr', pGraphics, "int", RenderingHint)

; Default = 0
; LowQuality = 1
; HighQuality = 2
; Bilinear = 3
; Bicubic = 4
; NearestNeighbor = 5
; HighQualityBilinear = 6
; HighQualityBicubic = 7
Gdip_SetInterpolationMode(pGraphics, InterpolationMode) =>
  DllCall("gdiplus\GdipSetInterpolationMode"
    , 'uptr', pGraphics, "int", InterpolationMode)


; Default = 0
; HighSpeed = 1
; HighQuality = 2
; None = 3
; AntiAlias = 4
Gdip_SetSmoothingMode(pGraphics, SmoothingMode) => DllCall("gdiplus\GdipSetSmoothingMode", 'uptr', pGraphics, "int", SmoothingMode)

; CompositingModeSourceOver = 0 (blended)
; CompositingModeSourceCopy = 1 (overwrite)
Gdip_SetCompositingMode(pGraphics, CompositingMode := 0) =>
  DllCall("gdiplus\GdipSetCompositingMode"
    , 'uptr', pGraphics, "int", CompositingMode)

; Extra functions

Gdip_Startup() {
  if !DllCall("GetModuleHandle", "str", "gdiplus", 'ptr')
    DllCall("LoadLibrary", "str", "gdiplus")
  VarSetStrCapacity(&si, A_PtrSize = 8 ? 24 : 16), si := Chr(1)

  DllCall("gdiplus\GdiplusStartup"
    , "uPtr*", &pToken := 0, 'ptr', StrPtr(si), 'ptr', 0)
  return pToken
}

Gdip_Shutdown(pToken) {
  DllCall("gdiplus\GdiplusShutdown", 'uptr', pToken)
  if hModule := DllCall("GetModuleHandle", "str", "gdiplus", 'uptr')
    DllCall("FreeLibrary", 'uptr', hModule)
  return 0
}

; Prepend = 0; The new operation is applied before the old operation.
; Append = 1; The new operation is applied after the old operation.

Gdip_RotateWorldTransform(pGraphics, Angle, MatrixOrder := 0) {
  return DllCall("gdiplus\GdipRotateWorldTransform"
    , 'uptr', pGraphics
    , "float", Angle, "int", MatrixOrder)
}
Gdip_ScaleWorldTransform(pGraphics, x, y, MatrixOrder := 0) {
  return DllCall("gdiplus\GdipScaleWorldTransform"
    , 'uptr', pGraphics
    , "float", x, "float", y, "int", MatrixOrder)
}

Gdip_TranslateWorldTransform(pGraphics, x, y, MatrixOrder := 0) {
  return DllCall("gdiplus\GdipTranslateWorldTransform"
    , 'uptr', pGraphics
    , "float", x, "float", y, "int", MatrixOrder)
}

Gdip_ResetWorldTransform(pGraphics) {
  return DllCall("gdiplus\GdipResetWorldTransform"
    , 'uptr', pGraphics)
}

Gdip_GetRotatedTranslation(Width, Height, Angle, &xTranslation, &yTranslation) {
  pi := 3.14159, TAngle := Angle * (pi / 180)
  Bound := (Angle >= 0) ? Mod(Angle, 360) : 360 - Mod(-Angle, -360)
  if ((Bound >= 0) && (Bound <= 90))
    xTranslation := Height * Sin(TAngle), yTranslation := 0
  else if ((Bound > 90) && (Bound <= 180))
    xTranslation := (Height * Sin(TAngle)) - (Width * Cos(TAngle)), yTranslation := -Height * Cos(TAngle)
  else if ((Bound > 180) && (Bound <= 270))
    xTranslation := -(Width * Cos(TAngle)), yTranslation := -(Height * Cos(TAngle)) - (Width * Sin(TAngle))
  else if ((Bound > 270) && (Bound <= 360))
    xTranslation := 0, yTranslation := -Width * Sin(TAngle)
}

Gdip_GetRotatedDimensions(Width, Height, Angle, &RWidth, &RHeight) {
  pi := 3.14159, TAngle := Angle * (pi / 180)
  if !(Width && Height)
    return -1
  RWidth := Ceil(Abs(Width * Cos(TAngle)) + Abs(Height * Sin(TAngle)))
  RHeight := Ceil(Abs(Width * Sin(TAngle)) + Abs(Height * Cos(Tangle)))
}


; RotateNoneFlipNone   = 0
; Rotate90FlipNone     = 1
; Rotate180FlipNone    = 2
; Rotate270FlipNone    = 3
; RotateNoneFlipX      = 4
; Rotate90FlipX        = 5
; Rotate180FlipX       = 6
; Rotate270FlipX       = 7
; RotateNoneFlipY      = Rotate180FlipX
; Rotate90FlipY        = Rotate270FlipX
; Rotate180FlipY       = RotateNoneFlipX
; Rotate270FlipY       = Rotate90FlipX
; RotateNoneFlipXY     = Rotate180FlipNone
; Rotate90FlipXY       = Rotate270FlipNone
; Rotate180FlipXY      = RotateNoneFlipNone
; Rotate270FlipXY      = Rotate90FlipNone
Gdip_ImageRotateFlip(pBitmap, RotateFlipType := 1) {
  return DllCall("gdiplus\GdipImageRotateFlip"
    , 'uptr', pBitmap, "int", RotateFlipType)
}


; Replace = 0
; Intersect = 1
; Union = 2
; Xor = 3
; Exclude = 4
; Complement = 5
Gdip_SetClipRect(pGraphics, x, y, w, h, CombineMode := 0) {
  return DllCall("gdiplus\GdipSetClipRect"
    , 'uptr', pGraphics
    , "float", x, "float", y, "float", w, "float", h, "int", CombineMode)
}

Gdip_SetClipPath(pGraphics, pPath, CombineMode := 0) => DllCall("gdiplus\GdipSetClipPath", 'uptr', pGraphics, 'uptr', pPath, "int", CombineMode)
Gdip_ResetClip(pGraphics) => DllCall("gdiplus\GdipResetClip", 'uptr', pGraphics)
Gdip_GetClipRegion(pGraphics) {
  Region := Gdip_CreateRegion()
  DllCall("gdiplus\GdipGetClip", 'uptr', pGraphics, "UInt", Region)
  return Region
}
Gdip_SetClipRegion(pGraphics, Region, CombineMode := 0) =>
  DllCall("gdiplus\GdipSetClipRegion", 'uptr', pGraphics, 'uptr', Region, "int", CombineMode)
Gdip_CreateRegion() {
  DllCall("gdiplus\GdipCreateRegion", "UInt*", &Region := 0)
  return Region
}
Gdip_DeleteRegion(Region) => DllCall("gdiplus\GdipDeleteRegion", 'uptr', Region)


; BitmapLockBits

Gdip_LockBits(pBitmap, x, y, w, h, &Stride, &Scan0, &BitmapData, LockMode := 3, PixelFormat := 0x26200a) {
  CreateRect(&_Rect, x, y, w, h)
  BitmapData := Buffer(16 + 2 * (A_PtrSize ? A_PtrSize : 4), 0)
  _E := DllCall("Gdiplus\GdipBitmapLockBits", 'uptr', pBitmap, 'uptr', _Rect.Ptr, "uint", LockMode, "int", PixelFormat, 'uptr', BitmapData.Ptr)
  Stride := NumGet(BitmapData, 8, "Int")
  Scan0 := NumGet(BitmapData, 16, 'uptr')
  return _E
}

GetBitsFromScreen(x, y, w, h, &Stride, &Scan0, &Bits, bpp := 32, _p := -1) {
  Bits := Buffer(w * h * 4, 0)
  ; 桌面窗口对应包含所有显示器的虚拟屏幕
  win := DllCall("GetDesktopWindow")
  hDC := DllCall("GetWindowDC", 'ptr', win)
  mDC := DllCall("CreateCompatibleDC", 'ptr', hDC)
  hBM := DllCall("CreateCompatibleBitmap", 'ptr', hDC, "int", w, "int", h)
  oBM := DllCall("SelectObject", 'ptr', mDC, 'ptr', hBM)
  DllCall("BitBlt", 'ptr', mDC, "int", 0, "int", 0, "int", w, "int", h, 'ptr', hDC, "int", x, "int", y, "uint", 0x00CC0020 | 0x40000000)

  bi := Buffer(40, 0)
  NumPut('int', 40, bi, 0)
  NumPut('int', w, bi, 4)
  NumPut('int', h * _p, bi, 8)
  NumPut('short', 1, bi, 12)
  NumPut('short', bpp, bi, 14)
  NumPut('int', 0, bi, 16)

  DllCall("GetDIBits", 'ptr', mDC, 'ptr', hBM, "int", 0, "int", h, 'ptr', Bits.Ptr, 'ptr', bi.Ptr, "int", 0)
  DllCall("SelectObject", 'ptr', mDC, 'ptr', oBM)
  DllCall("DeleteObject", 'ptr', hBM)
  DllCall("DeleteDC", 'ptr', mDC)
  DllCall("ReleaseDC", 'ptr', win, 'ptr', hDC)
  Scan0 := Bits.Ptr, Stride := ((w * bpp + 31) // 32) * 4
}


Gdip_UnlockBits(pBitmap, &BitmapData) => DllCall("Gdiplus\GdipBitmapUnlockBits", 'uptr', pBitmap, 'uptr', ObjPtr(BitmapData))
Gdip_SetLockBitPixel(ARGB, Scan0, x, y, Stride) => NumPut('uint', ARGB, Scan0 + 0, (x * 4) + (y * Stride))
Gdip_GetLockBitPixel(Scan0, x, y, Stride) => NumGet(Scan0 + 0, (x * 4) + (y * Stride), "UInt")

Gdip_PixelateBitmap(pBitmap, &pBitmapOut, BlockSize) {
  static PixelateBitmap := 0
  if (!PixelateBitmap) {
    if A_PtrSize != 8 ; x86 machine code
      MCode_PixelateBitmap := "
		(LTrim Join
		558BEC83EC3C8B4514538B5D1C99F7FB56578BC88955EC894DD885C90F8E830200008B451099F7FB8365DC008365E000894DC88955F08945E833FF897DD4
		397DE80F8E160100008BCB0FAFCB894DCC33C08945F88945FC89451C8945143BD87E608B45088D50028BC82BCA8BF02BF2418945F48B45E02955F4894DC4
		8D0CB80FAFCB03CA895DD08BD1895DE40FB64416030145140FB60201451C8B45C40FB604100145FC8B45F40FB604020145F883C204FF4DE475D6034D18FF
		4DD075C98B4DCC8B451499F7F98945148B451C99F7F989451C8B45FC99F7F98945FC8B45F899F7F98945F885DB7E648B450C8D50028BC82BCA83C103894D
		C48BC82BCA41894DF48B4DD48945E48B45E02955E48D0C880FAFCB03CA895DD08BD18BF38A45148B7DC48804178A451C8B7DF488028A45FC8804178A45F8
		8B7DE488043A83C2044E75DA034D18FF4DD075CE8B4DCC8B7DD447897DD43B7DE80F8CF2FEFFFF837DF0000F842C01000033C08945F88945FC89451C8945
		148945E43BD87E65837DF0007E578B4DDC034DE48B75E80FAF4D180FAFF38B45088D500203CA8D0CB18BF08BF88945F48B45F02BF22BFA2955F48945CC0F
		B6440E030145140FB60101451C0FB6440F010145FC8B45F40FB604010145F883C104FF4DCC75D8FF45E4395DE47C9B8B4DF00FAFCB85C9740B8B451499F7
		F9894514EB048365140033F63BCE740B8B451C99F7F989451CEB0389751C3BCE740B8B45FC99F7F98945FCEB038975FC3BCE740B8B45F899F7F98945F8EB
		038975F88975E43BDE7E5A837DF0007E4C8B4DDC034DE48B75E80FAF4D180FAFF38B450C8D500203CA8D0CB18BF08BF82BF22BFA2BC28B55F08955CC8A55
		1488540E038A551C88118A55FC88540F018A55F888140183C104FF4DCC75DFFF45E4395DE47CA68B45180145E0015DDCFF4DC80F8594FDFFFF8B451099F7
		FB8955F08945E885C00F8E450100008B45EC0FAFC38365DC008945D48B45E88945CC33C08945F88945FC89451C8945148945103945EC7E6085DB7E518B4D
		D88B45080FAFCB034D108D50020FAF4D18034DDC8BF08BF88945F403CA2BF22BFA2955F4895DC80FB6440E030145140FB60101451C0FB6440F010145FC8B
		45F40FB604080145F883C104FF4DC875D8FF45108B45103B45EC7CA08B4DD485C9740B8B451499F7F9894514EB048365140033F63BCE740B8B451C99F7F9
		89451CEB0389751C3BCE740B8B45FC99F7F98945FCEB038975FC3BCE740B8B45F899F7F98945F8EB038975F88975103975EC7E5585DB7E468B4DD88B450C
		0FAFCB034D108D50020FAF4D18034DDC8BF08BF803CA2BF22BFA2BC2895DC88A551488540E038A551C88118A55FC88540F018A55F888140183C104FF4DC8
		75DFFF45108B45103B45EC7CAB8BC3C1E0020145DCFF4DCC0F85CEFEFFFF8B4DEC33C08945F88945FC89451C8945148945103BC87E6C3945F07E5C8B4DD8
		8B75E80FAFCB034D100FAFF30FAF4D188B45088D500203CA8D0CB18BF08BF88945F48B45F02BF22BFA2955F48945C80FB6440E030145140FB60101451C0F
		B6440F010145FC8B45F40FB604010145F883C104FF4DC875D833C0FF45108B4DEC394D107C940FAF4DF03BC874068B451499F7F933F68945143BCE740B8B
		451C99F7F989451CEB0389751C3BCE740B8B45FC99F7F98945FCEB038975FC3BCE740B8B45F899F7F98945F8EB038975F88975083975EC7E63EB0233F639
		75F07E4F8B4DD88B75E80FAFCB034D080FAFF30FAF4D188B450C8D500203CA8D0CB18BF08BF82BF22BFA2BC28B55F08955108A551488540E038A551C8811
		8A55FC88540F018A55F888140883C104FF4D1075DFFF45088B45083B45EC7C9F5F5E33C05BC9C21800
		)"
    else ; x64 machine code
      MCode_PixelateBitmap := "
		(LTrim Join
		4489442418488954241048894C24085355565741544155415641574883EC28418BC1448B8C24980000004C8BDA99488BD941F7F9448BD0448BFA8954240C
		448994248800000085C00F8E9D020000418BC04533E4458BF299448924244C8954241041F7F933C9898C24980000008BEA89542404448BE889442408EB05
		4C8B5C24784585ED0F8E1A010000458BF1418BFD48897C2418450FAFF14533D233F633ED4533E44533ED4585C97E5B4C63BC2490000000418D040A410FAF
		C148984C8D441802498BD9498BD04D8BD90FB642010FB64AFF4403E80FB60203E90FB64AFE4883C2044403E003F149FFCB75DE4D03C748FFCB75D0488B7C
		24188B8C24980000004C8B5C2478418BC59941F7FE448BE8418BC49941F7FE448BE08BC59941F7FE8BE88BC69941F7FE8BF04585C97E4048639C24900000
		004103CA4D8BC1410FAFC94863C94A8D541902488BCA498BC144886901448821408869FF408871FE4883C10448FFC875E84803D349FFC875DA8B8C249800
		0000488B5C24704C8B5C24784183C20448FFCF48897C24180F850AFFFFFF8B6C2404448B2424448B6C24084C8B74241085ED0F840A01000033FF33DB4533
		DB4533D24533C04585C97E53488B74247085ED7E42438D0C04418BC50FAF8C2490000000410FAFC18D04814863C8488D5431028BCD0FB642014403D00FB6
		024883C2044403D80FB642FB03D80FB642FA03F848FFC975DE41FFC0453BC17CB28BCD410FAFC985C9740A418BC299F7F98BF0EB0233F685C9740B418BC3
		99F7F9448BD8EB034533DB85C9740A8BC399F7F9448BD0EB034533D285C9740A8BC799F7F9448BC0EB034533C033D24585C97E4D4C8B74247885ED7E3841
		8D0C14418BC50FAF8C2490000000410FAFC18D04814863C84A8D4431028BCD40887001448818448850FF448840FE4883C00448FFC975E8FFC2413BD17CBD
		4C8B7424108B8C2498000000038C2490000000488B5C24704503E149FFCE44892424898C24980000004C897424100F859EFDFFFF448B7C240C448B842480
		000000418BC09941F7F98BE8448BEA89942498000000896C240C85C00F8E3B010000448BAC2488000000418BCF448BF5410FAFC9898C248000000033FF33
		ED33F64533DB4533D24533C04585FF7E524585C97E40418BC5410FAFC14103C00FAF84249000000003C74898488D541802498BD90FB642014403D00FB602
		4883C2044403D80FB642FB03F00FB642FA03E848FFCB75DE488B5C247041FFC0453BC77CAE85C9740B418BC299F7F9448BE0EB034533E485C9740A418BC3
		99F7F98BD8EB0233DB85C9740A8BC699F7F9448BD8EB034533DB85C9740A8BC599F7F9448BD0EB034533D24533C04585FF7E4E488B4C24784585C97E3541
		8BC5410FAFC14103C00FAF84249000000003C74898488D540802498BC144886201881A44885AFF448852FE4883C20448FFC875E941FFC0453BC77CBE8B8C
		2480000000488B5C2470418BC1C1E00203F849FFCE0F85ECFEFFFF448BAC24980000008B6C240C448BA4248800000033FF33DB4533DB4533D24533C04585
		FF7E5A488B7424704585ED7E48418BCC8BC5410FAFC94103C80FAF8C2490000000410FAFC18D04814863C8488D543102418BCD0FB642014403D00FB60248
		83C2044403D80FB642FB03D80FB642FA03F848FFC975DE41FFC0453BC77CAB418BCF410FAFCD85C9740A418BC299F7F98BF0EB0233F685C9740B418BC399
		F7F9448BD8EB034533DB85C9740A8BC399F7F9448BD0EB034533D285C9740A8BC799F7F9448BC0EB034533C033D24585FF7E4E4585ED7E42418BCC8BC541
		0FAFC903CA0FAF8C2490000000410FAFC18D04814863C8488B442478488D440102418BCD40887001448818448850FF448840FE4883C00448FFC975E8FFC2
		413BD77CB233C04883C428415F415E415D415C5F5E5D5BC3
		)"

    PixelateBitmap := Buffer(StrLen(MCode_PixelateBitmap) // 2)
    nCount := StrLen(MCode_PixelateBitmap) // 2
    Loop nCount {
      NumPut('uchar', "0x" SubStr(MCode_PixelateBitmap, (2 * A_Index) - 1, 2), PixelateBitmap, A_Index - 1)
    }
    DllCall("VirtualProtect", 'uptr', PixelateBitmap.Ptr, 'uptr', PixelateBitmap.Size, "uint", 0x40, 'uptr*', 0)
  }
  Gdip_GetImageDimensions(pBitmap, &Width, &Height)

  if (Width != Gdip_GetImageWidth(pBitmapOut) || Height != Gdip_GetImageHeight(pBitmapOut))
    return -1
  if (BlockSize > Width || BlockSize > Height)
    return -2

  E1 := Gdip_LockBits(pBitmap, 0, 0, Width, Height, &Stride1, &Scan01, &BitmapData1)
  E2 := Gdip_LockBits(pBitmapOut, 0, 0, Width, Height, &Stride2, &Scan02, &BitmapData2)
  if (E1 || E2)
    return -3
  DllCall(PixelateBitmap.Ptr, 'uptr', Scan01, 'uptr', Scan02, "int", Width, "int", Height, "int", Stride1, "int", BlockSize)

  Gdip_UnlockBits(pBitmap, &BitmapData1), Gdip_UnlockBits(pBitmapOut, &BitmapData2)
  return 0
}

Gdip_ToARGB(A, R, G, B) => (A << 24) | (R << 16) | (G << 8) | B
Gdip_FromARGB(ARGB, &A, &R, &G, &B) {
  A := (0xff000000 & ARGB) >> 24
  R := (0x00ff0000 & ARGB) >> 16
  G := (0x0000ff00 & ARGB) >> 8
  B := 0x000000ff & ARGB
}
Gdip_AFromARGB(ARGB) => (0xff000000 & ARGB) >> 24
Gdip_RFromARGB(ARGB) => (0x00ff0000 & ARGB) >> 16
Gdip_GFromARGB(ARGB) => (0x0000ff00 & ARGB) >> 8
Gdip_BFromARGB(ARGB) => 0x000000ff & ARGB

StrGetB(Address, Length := -1, Encoding := 0) {
  ; Flexible parameter handling:
  if !IsInteger(Length)
    Encoding := Length, Length := -1

  ; Check for obvious errors.
  if (Address + 0 < 1024)
    return

  ; Ensure 'Encoding' contains a numeric identifier.
  if (Encoding = "UTF-16")
    Encoding := 1200
  else if (Encoding = "UTF-8")
    Encoding := 65001
  else if SubStr(Encoding, 1, 2) = "CP"
    Encoding := SubStr(Encoding, 3)

  if !Encoding { ; "" or 0
    ; No conversion necessary, but we might not want the whole string.
    if (Length == -1)
      Length := DllCall("lstrlen", "uint", Address)
    String := Buffer(Length)
    DllCall("lstrcpyn", "str", StrGet(String), "uint", Address, "int", Length + 1)
  } else if (Encoding = 1200) { ; UTF-16
    char_count := DllCall("WideCharToMultiByte"
      , "uint", 0, "uint", 0x400, "uint", Address, "int", Length
      , "uint", 0, "uint", 0, "uint", 0, "uint", 0)
    String := Buffer(char_count)
    DllCall("WideCharToMultiByte"
      , "uint", 0, "uint", 0x400, "uint", Address, "int", Length
      , "str", StrGet(String), "int", char_count, "uint", 0, "uint", 0)
  }
  else if IsInteger(Encoding) {
    ; Convert from target encoding to UTF-16 then to the active code page.
    char_count := DllCall("MultiByteToWideChar"
      , "uint", Encoding, "uint", 0, "uint", Address, "int", Length, "uint", 0, "int", 0)
    String := Buffer(char_count * 2)
    char_count := DllCall("MultiByteToWideChar"
      , "uint", Encoding, "uint", 0, "uint", Address, "int", Length, "uint", String.Ptr, "int", char_count * 2)
    String := StrGetB(&String, char_count, 1200)
  }

  return String
}

; Based on WinGetClientPos by dd900 and Frosti - https://www.autohotkey.com/boards/viewtopic.php?t=484
WinGetRect(hwnd, &x := "", &y := "", &w := "", &h := "") {
  CreateRect(&winRect, 0, 0, 0, 0) ;is 16 on both 32 and 64
  ;VarSetCapacity( winRect, 16, 0 )	; Alternative of above two lines
  DllCall("GetWindowRect", 'uptr', hwnd, 'uptr', winRect.Ptr)
  x := NumGet(winRect, 0, "UInt")
  y := NumGet(winRect, 4, "UInt")
  w := NumGet(winRect, 8, "UInt") - x
  h := NumGet(winRect, 12, "UInt") - y
}